Merge lp:~vorlon/bzr-email/extra-headers into lp:bzr-email

Proposed by Steve Langasek
Status: Merged
Merged at revision: not available
Proposed branch: lp:~vorlon/bzr-email/extra-headers
Merge into: lp:bzr-email
Diff against target: 139 lines (+40/-7)
4 files modified
__init__.py (+3/-0)
emailer.py (+15/-1)
smtp_connection.py (+16/-5)
tests/testemail.py (+6/-1)
To merge this branch: bzr merge lp:~vorlon/bzr-email/extra-headers
Reviewer Review Type Date Requested Status
Robert Collins Pending
Review via email: mp+21071@code.launchpad.net

This proposal supersedes a proposal from 2010-03-09.

Description of the change

Add support for including custom headers in generated mails, necessary for interfacing with the Debian PTS

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote : Posted in a previous version of this proposal

 review: needsfixing

One crucial thing, three smaller.

Crucially: please call the option
revision_mail_headers
and document it in the docstring in __init__.py - thats where the help
for 'bzr help email' is sourced. I think there are some docs in README
too, that are duplicated but should be kept in sync.

Smaller things:
Characters are nearly free: s/xhdrs/extra_mail_headers/ throughout
please: the 8 bit days are long gone ;)

A test case for this would be great.

In the sending function you do
xhdrs = self.extra_headers()
....
  (
   , xhdrs)

and don't use the variable at all. Please just call it when you call the
lower level function.

-Rob

review: Needs Fixing
Revision history for this message
Robert Collins (lifeless) wrote :

Thanks for this. There were some more nits:

Your docs were not enough to show someone how to set a header, so I've
improved on that.

Please set your editor not to use tabs when editing python code.

+ try:
+ key,value = line.split(": ")
+ result[key] = value
+ except ValueError: pass

Is something I missed - it seems to imply silently not doing what we
have been asked to do, so I'm deleting the try/except here.
Alternatively we could mutter it to the log.

In the same region, types should be compared with 'is', not ==.

try:
    for key in extra_mail_headers:
       msg[key] = extra_mail_headers[key]
except TypeError: pass

is a problem - a type error here could mean several different things, so
I've rewritten it to be safer.

In a related thing, its unidiomatic to do
except TypeError: pass
rather than
except TypeError:
    pass

Thanks again for this. Tests still pass as I am committing this, but I
note you didn't add a test that your extra headers /get/ all the way
through, so you may wish to do a manual check of trunk now.

-Rob

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '__init__.py'
2--- __init__.py 2008-12-09 20:09:15 +0000
3+++ __init__.py 2010-03-10 18:38:20 +0000
4@@ -58,6 +58,9 @@
5 - Any other value: Run the value expecting it to behave like ``/usr/bin/mail``
6 - in particular supporting the -s and -a options.
7
8+When using smtplib, you can specify additional headers to be included in the
9+mail by setting the 'revision_mail_headers' configuration option.
10+
11 """
12
13
14
15=== modified file 'emailer.py'
16--- emailer.py 2008-12-09 20:09:15 +0000
17+++ emailer.py 2010-03-10 18:38:20 +0000
18@@ -167,6 +167,19 @@
19 result = self.config.username()
20 return result
21
22+ def extra_headers(self):
23+ """Additional headers to include when sending."""
24+ result = {}
25+ headers = self.config.get_user_option('revision_mail_headers')
26+ if type(headers) != list:
27+ headers = [headers]
28+ for line in headers:
29+ try:
30+ key,value = line.split(": ")
31+ result[key] = value
32+ except ValueError: pass
33+ return result
34+
35 def send(self):
36 """Send the email.
37
38@@ -229,7 +242,8 @@
39 smtp = self._smtplib_implementation(self.config)
40 smtp.send_text_and_attachment_email(from_addr, to_addrs,
41 subject, body, diff,
42- self.diff_filename())
43+ self.diff_filename(),
44+ self.extra_headers())
45
46 def should_send(self):
47 result = self.config.get_user_option('post_commit_difflimit')
48
49=== modified file 'smtp_connection.py'
50--- smtp_connection.py 2009-03-10 02:24:28 +0000
51+++ smtp_connection.py 2010-03-10 18:38:20 +0000
52@@ -123,7 +123,8 @@
53 """
54 return parseaddr(address)
55
56- def _basic_message(self, from_address, to_addresses, subject):
57+ def _basic_message(self, from_address, to_addresses, subject,
58+ extra_mail_headers=None):
59 """Create the basic Message using the right Header info.
60
61 This creates an email Message with no payload.
62@@ -146,6 +147,12 @@
63 msg['From'] = '%s <%s>' % (Header(unicode(from_user)), from_email)
64 msg['User-Agent'] = 'bzr/%s' % _bzrlib_version
65
66+ # MIMEMultipart doesn't support update()
67+ try:
68+ for key in extra_mail_headers:
69+ msg[key] = extra_mail_headers[key]
70+ except TypeError: pass
71+
72 to_emails = []
73 to_header = []
74 for addr in to_addresses:
75@@ -157,7 +164,8 @@
76 msg['Subject'] = Header(subject)
77 return msg, from_email, to_emails
78
79- def create_email(self, from_address, to_addresses, subject, text):
80+ def create_email(self, from_address, to_addresses, subject, text,
81+ extra_mail_headers=None):
82 """Create an email.Message object.
83
84 This function allows you to create a basic email, and then add extra
85@@ -178,7 +186,8 @@
86 to_emails: the list of email addresses extracted from to_addresses
87 """
88 msg, from_email, to_emails = self._basic_message(from_address,
89- to_addresses, subject)
90+ to_addresses, subject,
91+ extra_mail_headers)
92 payload = MIMEText(text.encode('utf-8'), 'plain', 'utf-8')
93 msg.attach(payload)
94 return msg, from_email, to_emails
95@@ -214,7 +223,8 @@
96
97 def send_text_and_attachment_email(self, from_address, to_addresses,
98 subject, message, attachment_text,
99- attachment_filename='patch.diff'):
100+ attachment_filename='patch.diff',
101+ extra_mail_headers=None):
102 """Send a Unicode message and an 8-bit attachment.
103
104 See create_email for common parameter definitions.
105@@ -225,7 +235,8 @@
106 give a default name for email programs to save the attachment.
107 """
108 msg, from_email, to_emails = self.create_email(from_address,
109- to_addresses, subject, message)
110+ to_addresses, subject, message,
111+ extra_mail_headers)
112 # Must be an 8-bit string
113 assert isinstance(attachment_text, str)
114
115
116=== modified file 'tests/testemail.py'
117--- tests/testemail.py 2008-12-09 20:09:15 +0000
118+++ tests/testemail.py 2010-03-10 18:38:20 +0000
119@@ -34,7 +34,8 @@
120
121 sample_config=("[DEFAULT]\n"
122 "post_commit_to=demo@example.com\n"
123- "post_commit_sender=Sample <foo@example.com>\n")
124+ "post_commit_sender=Sample <foo@example.com>\n"
125+ "revision_mail_headers=X-Cheese: to the rescue!\n")
126
127 unconfigured_config=("[DEFAULT]\n"
128 "email=Robert <foo@example.com>\n")
129@@ -150,6 +151,10 @@
130 sender = self.get_sender()
131 self.assertEqual('patch-1.diff', sender.diff_filename())
132
133+ def test_headers(self):
134+ sender = self.get_sender()
135+ self.assertEqual({'X-Cheese': 'to the rescue!'}, sender.extra_headers())
136+
137 def get_sender(self, text=sample_config):
138 self.branch = BzrDir.create_branch_convenience('.')
139 tree = self.branch.bzrdir.open_workingtree()

Subscribers

People subscribed via source and target branches

to all changes: