Reputation: 64719
I'm trying to write unittests for some custom Django email backends, and to test it against a "real" smtp server, I'm trying to use Python's built-in smtpd debugging server by running:
python -m smtpd -n -c DebuggingServer localhost:1025
My unittest basically looks like:
class Tests(TestCase):
@override_settings(EMAIL_BACKEND='mycustombackend')
@override_settings(EMAIL_HOST='localhost')
@override_settings(EMAIL_PORT='1025')
def test_backend(self):
from django.core import mail
mail.send_mail(
subject='Subject here',
message='Here is the message.',
from_email='[email protected]',
recipient_list=['[email protected]'],
fail_silently=False,
)
and when I run this, the smtpd process outputs the email content correctly.
However, when I try and capture that so I can confirm it in my unittest, I get nothing. I've tried using the subprocess
package, to launch the process and read the output via pipes, but it never receives any output.
I thought I was using subprocess incorrectly, so as a last resort, I tried launching the process with:
python -m smtpd -n -c DebuggingServer localhost:1025 > /tmp/smtpd.log
and reading the log file. However, even with that, no output is ever written to the file.
What's going on here?
Upvotes: 6
Views: 7491
Reputation: 74
When working with Flask, I set up a similar SMPTHandler for app.logger.
I found that the output of python -m smptd
goes to the stderr
(&2), instead of stdout
(&1). Although there may be more elegant solutions, this worked for me:
python -m smtpd -n -c DebuggingServer 127.0.0.1:1025 2>&1 >> smtp_error.log
The command sends the stderr
to stdout
and then logs all output to the smpt_error.log.
Upvotes: 0
Reputation: 94299
I noticed the same symptom - for me, it helped to pass the -u
argument to the Python interpreter, as in:
python -u -m smtpd -n -c DebuggingServer localhost:1025
The documentation says:
Force stdin, stdout and stderr to be totally unbuffered. On systems where it matters, also put stdin, stdout and stderr in binary mode.
Upvotes: 3
Reputation: 2568
According to this answer, output buffering is turned on when:
process STDOUT is redirected to something other than a terminal
The suggested solution would in this case be:
stdbuf -oL python -m smtpd -n -c DebuggingServer localhost:1025 > mail.log
Upvotes: 0
Reputation: 51
I had the same problem and spent 2 days trying to figure out what's going on. I tried running both
python -m smtpd -n -c DebuggingServer localhost:1025
and
python -m smtpd -n -c DebuggingServer localhost:1025 > mail.log
from one of my integration tests with subprocess
, but it didn't work. While experimenting with it through REPL I noticed that first read from the pipe that is opened for us by subprocess
hangs. After I kill it, next read actually returns the data. So I started investigating what's in the stream. But as I had no luck in 2 hours, I ended up rolling my own wrapper around SMTPServer
that writes to the file, and got myself up and running.
Here is the wrapper class (process_message
is an abstract method that is required of smtpd.SMTPServer
's child class to be runnable by smtpd
module):
# test_smtpd.py
import smtpd
SMTP_DUMPFILE = '/tmp/mail.log'
class SMTPTestServer(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
with open(SMTP_DUMPFILE, 'w') as f:
f.write(data)
I run it with
python -m smtpd -n -c test_smtpd.SMTPTestServer localhost:1025
While this does not directly answer your question, it is an easy workaround, so I hope this helps.
Upvotes: 3