如何从Python smtp调试服务器捕获输出

时间:2016-10-20 00:12:13

标签: python django email

我正在尝试为一些自定义的Django电子邮件后端编写单元测试,并针对“真正的”smtp服务器进行测试,我试图通过运行来使用Python的内置smtpd调试服务器:

python -m smtpd -n -c DebuggingServer localhost:1025

我的unittest基本上看起来像:

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='from@example.com',
                recipient_list=['to@example.com'],
                fail_silently=False,
            )

当我运行它时,smtpd进程正确输出电子邮件内容。

但是,当我尝试捕获它时,我可以在我的单元测试中确认它,我什么也得不到。我已经尝试使用subprocess包来启动进程并通过管道读取输出,但它从不接收任何输出。

我以为我错误地使用了子进程,所以作为最后的手段,我尝试使用以下命令启动进程:

python -m smtpd -n -c DebuggingServer localhost:1025 > /tmp/smtpd.log

并读取日志文件。但是,即使这样,也不会将任何输出写入文件。

这里发生了什么?

4 个答案:

答案 0 :(得分:3)

我遇到了同样的问题,花了2天时间试图找出发生了什么。我试过跑两个

python -m smtpd -n -c DebuggingServer localhost:1025

python -m smtpd -n -c DebuggingServer localhost:1025 > mail.log

来自我与subprocess的一次集成测试,但它没有用。在通过REPL进行实验时,我注意到subprocess为我们打开的管道中的第一个读取挂起。我杀了之后,下一次读取实际上会返回数据。所以我开始调查流中的内容。但是因为我在2个小时内没有运气,所以我最终在SMTPServer周围滚动自己的包装器,写入文件,然后让自己运转起来。

这是包装类(process_messagesmtpd.SMTPServer模块可以运行的smtpd子类所需的抽象方法:

# 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)

我用

运行它
python -m smtpd -n -c test_smtpd.SMTPTestServer localhost:1025

虽然这不能直接回答你的问题,但这是一个简单的解决方法,所以我希望这会有所帮助。

答案 1 :(得分:1)

我注意到了相同的症状 - 对我来说,它有助于将-u参数传递给Python解释器,如:

python -u -m smtpd -n -c DebuggingServer localhost:1025

documentation说:

  

强制stdin,stdout和stderr完全无缓冲。在重要的系统上,还将stdin,stdout和stderr置于二进制模式。

答案 2 :(得分:0)

根据this answer,输出缓冲在以下时间打开:

  

进程STDOUT被重定向到终端

以外的其他东西

在这种情况下,建议的解决方案是:

stdbuf -oL python -m smtpd -n -c DebuggingServer localhost:1025 > mail.log

答案 3 :(得分:0)

在使用 Flask 时,我为 app.logger 设置了一个类似的 SMPTHandler。

我发现 HasSensitivityClassification = Confidential 的输出转到 Starting Step #0 Step #0: Pulling image: mirror.gcr.io/library/golang Step #0: Using default tag: latest Step #0: Error response from daemon: manifest for mirror.gcr.io/library/golang:latest not found: manifest unknown: Failed to fetch "latest" from request "/v2/library/golang/manifests/latest". ... Step #0: Error response from daemon: manifest for mirror.gcr.io/library/golang:latest not found: manifest unknown: Failed to fetch "latest" from request "/v2/library/golang/manifests/latest". ERROR: failed to pull because we ran out of retries. ERROR ERROR: build step 0 "mirror.gcr.io/library/golang" failed: error pulling build step 0 "mirror.gcr.io/library/golang": generic::unknown: retry budget exhausted (10 attempts): step exited with non-zero status: 1 (&2),而不是 python -m smptd (&1)。虽然可能有更优雅的解决方案,但这对我有用:

python -m smtpd -n -c DebuggingServer 127.0.0.1:1025 2>&1 >> smtp_error.log

该命令将 stderr 发送到 stdout,然后将所有输出记录到 smpt_error.log。