Merge lp:~edwin-grubbs/bzr/bug-384158-passing-body-to-mutt into lp:~bzr/bzr/trunk-old

Proposed by Edwin Grubbs
Status: Superseded
Proposed branch: lp:~edwin-grubbs/bzr/bug-384158-passing-body-to-mutt
Merge into: lp:~bzr/bzr/trunk-old
Diff against target: 53 lines
To merge this branch: bzr merge lp:~edwin-grubbs/bzr/bug-384158-passing-body-to-mutt
Reviewer Review Type Date Requested Status
John A Meinel Approve
bzr-core code Pending
Review via email: mp+7142@code.launchpad.net

This proposal has been superseded by a proposal from 2009-06-08.

To post a comment you must log in.
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

This branch adds the ability to pass the email body into mutt. I tried using a NamedTemporaryFile as Martin suggested, but the file got deleted immediately by garbage collection.

Revision history for this message
Martin Pool (mbp) wrote :

> This branch adds the ability to pass the email body into mutt. I tried using a
> NamedTemporaryFile as Martin suggested, but the file got deleted immediately
> by garbage collection.

Is that because bzr send exits before mutt is finished? If not, is it possible to hold a reference to the variable, but perhaps there's no easy scope to do so. It's not a big deal, but having a comment might be good.

Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

> > This branch adds the ability to pass the email body into mutt. I tried using
> a
> > NamedTemporaryFile as Martin suggested, but the file got deleted immediately
> > by garbage collection.
>
> Is that because bzr send exits before mutt is finished? If not, is it
> possible to hold a reference to the variable, but perhaps there's no easy
> scope to do so. It's not a big deal, but having a comment might be good.

I added a comment. Here is the incremental diff:

{{{
=== modified file 'bzrlib/mail_client.py'
--- bzrlib/mail_client.py 2009-06-06 03:25:19 +0000
+++ bzrlib/mail_client.py 2009-06-07 01:16:59 +0000
@@ -271,6 +271,9 @@
             message_options.extend(['-a',
                 self._encode_path(attach_path, 'attachment')])
         if body is not None:
+ # mkstemp() is used instead of NamedTemporaryFile, so that
+ # the file won't be deleted when the object gets garbage
+ # collected, which may be before mutt can read it.
             fd, temp_file = tempfile.mkstemp(prefix="mutt-body-",
                                              suffix=".txt")
             try:

}}}

Revision history for this message
John A Meinel (jameinel) wrote :

So I think to do this correctly, we would need to have a way to define the lifetime of the NamedTemporaryFile, versus the lifetime of running the mutt command.

I *think* if you just assigned the temp file to 'self' then it would survive long enough for mutt to find it.

If you just did

temp_file = NamedTemporaryFile(...)
temp_file.write()
...

args.extend(...)
...
return args

Then the temp file will be destroyed as soon as the function returns.

However if you did:

self._temp_file = NamedTemporaryFile(...)

then the file will be around until the Mutt class is destroyed, which should be long enough.

I'll also note that NamedTemporaryFile won't work on windows (it gives you the path to a file, but that file cannot be opened even within the same process... :(

Even so, I'm not worried about people using mutt on Windows, so I'd still like to try.

Anyway, I think this is good enough to merge, but it would be nice if you could try the NamedTemporaryFile trick again, with a longer lifetime.

review: Approve
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

Storing the temp file in the Mutt object worked. Here is the incremental diff.

=== modified file 'bzrlib/mail_client.py'
--- bzrlib/mail_client.py 2009-06-07 01:17:13 +0000
+++ bzrlib/mail_client.py 2009-06-08 17:43:56 +0000
@@ -271,16 +271,13 @@
             message_options.extend(['-a',
                 self._encode_path(attach_path, 'attachment')])
         if body is not None:
- # mkstemp() is used instead of NamedTemporaryFile, so that
- # the file won't be deleted when the object gets garbage
- # collected, which may be before mutt can read it.
- fd, temp_file = tempfile.mkstemp(prefix="mutt-body-",
- suffix=".txt")
- try:
- os.write(fd, body)
- finally:
- os.close(fd)
- message_options.extend(['-i', temp_file])
+ # Store the temp file object in self, so that it does not get
+ # garbage collected and delete the file before mutt can read it.
+ self._temp_file = tempfile.NamedTemporaryFile(
+ prefix="mutt-body-", suffix=".txt")
+ self._temp_file.write(body)
+ self._temp_file.flush()
+ message_options.extend(['-i', self._temp_file.name])
         if to is not None:
             message_options.extend(['--', self._encode_safe(to)])
         return message_options

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/mail_client.py'
2--- bzrlib/mail_client.py 2009-06-04 21:05:44 +0000
3+++ bzrlib/mail_client.py 2009-06-07 01:35:09 +0000
4@@ -257,12 +257,12 @@
5 help=Evolution.__doc__)
6
7
8-class Mutt(ExternalMailClient):
9+class Mutt(BodyExternalMailClient):
10 """Mutt mail client."""
11
12 _client_commands = ['mutt']
13
14- def _get_compose_commandline(self, to, subject, attach_path):
15+ def _get_compose_commandline(self, to, subject, attach_path, body=None):
16 """See ExternalMailClient._get_compose_commandline"""
17 message_options = []
18 if subject is not None:
19@@ -270,6 +270,17 @@
20 if attach_path is not None:
21 message_options.extend(['-a',
22 self._encode_path(attach_path, 'attachment')])
23+ if body is not None:
24+ # mkstemp() is used instead of NamedTemporaryFile, so that
25+ # the file won't be deleted when the object gets garbage
26+ # collected, which may be before mutt can read it.
27+ fd, temp_file = tempfile.mkstemp(prefix="mutt-body-",
28+ suffix=".txt")
29+ try:
30+ os.write(fd, body)
31+ finally:
32+ os.close(fd)
33+ message_options.extend(['-i', temp_file])
34 if to is not None:
35 message_options.extend(['--', self._encode_safe(to)])
36 return message_options
37
38=== modified file 'bzrlib/tests/test_mail_client.py'
39--- bzrlib/tests/test_mail_client.py 2009-06-04 19:56:42 +0000
40+++ bzrlib/tests/test_mail_client.py 2009-06-07 01:35:09 +0000
41@@ -28,8 +28,10 @@
42
43 def test_commandline(self):
44 mutt = mail_client.Mutt(None)
45- commandline = mutt._get_compose_commandline(None, None, 'file%')
46- self.assertEqual(['-a', 'file%'], commandline)
47+ commandline = mutt._get_compose_commandline(
48+ None, None, 'file%', body="hello")
49+ # The temporary filename is randomly generated, so it is not matched.
50+ self.assertEqual(['-a', 'file%', '-i'], commandline[:-1])
51 commandline = mutt._get_compose_commandline('jrandom@example.org',
52 'Hi there!', None)
53 self.assertEqual(['-s', 'Hi there!', '--', 'jrandom@example.org'],