You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

aiosmtpd本地服务器无响应,报TypeError: 'Controller'对象不可调用

问题分析:SMTP服务器连接报错TypeError: 'Controller' object is not callable

问题场景

编写了基于aiosmtpd的SMTP服务器用于打印收件人传入的消息,同时配套了客户端发送邮件脚本。启动服务器后终端显示It works!,但运行客户端连接时,服务器抛出如下错误:

Traceback (most recent call last):
File "C:\Users\Lev\AppData\Local\Programs\Python\Python311\Lib\asyncio\events.py", line 80, in _run
self._context.run(self._callback, *self._args)
File "C:\Users\Lev\AppData\Local\Programs\Python\Python311\Lib\asyncio\proactor_events.py", line 847, in loop
protocol = protocol_factory()
TypeError: 'Controller' object is not callable

错误原因

  1. 缩进错误:服务器代码中,Controller初始化、事件循环相关代码被错误写在ExampleHandler类内部,属于类属性定义,会在类加载时执行,而非程序运行时的服务器启动逻辑。
  2. Controller使用方式错误:asyncio.create_server需要传入可调用的Protocol工厂(比如类或函数),但直接传入了Controller实例,而Controller本身不是可调用对象,导致客户端连接时触发类型错误。

修正方案

修正后的服务器代码

import asyncio
from aiosmtpd.controller import Controller

class ExampleHandler:
    async def handle_RCPT(self, server, session, envelope, address, rcpt_options):
        if not address.endswith('@example.com'):
            return '550 not relaying to that domain'
        envelope.rcpt_tos.append(address)
        return '250 OK'

    async def handle_DATA(self, server, session, envelope):
        print('Message from %s' % envelope.mail_from)
        print('Message for %s' % envelope.rcpt_tos)
        print('Message data:\n')
        for ln in envelope.content.decode('utf8', errors='replace').splitlines():
            print(f'> {ln}'.strip())
        print()
        print('End of message')
        return '250 Message accepted for delivery'

# 将服务器启动逻辑移到类外部
handler = ExampleHandler()
# 初始化Controller时直接指定host和port
controller = Controller(handler, host='::1', port=8025)

loop = asyncio.get_event_loop()
controller.start()
try:
    print("SMTP server running on ::1:8025")
    loop.run_forever()
finally:
    controller.stop()

关键修正点

  • 调整缩进:把服务器启动相关代码从ExampleHandler类中移出,放在全局作用域,确保类定义和业务逻辑分离。
  • 正确使用Controller:使用Controller自带的start()方法启动服务器,该方法会自动处理与asyncio事件循环的绑定,无需手动调用loop.create_server。如果一定要手动创建服务器,需传入controller.factory(这才是符合create_server要求的可调用工厂):
    # 替代controller.start()和loop.run_forever()的写法
    server = loop.create_server(controller.factory, host='::1', port=8025)
    loop.run_until_complete(server)
    print("SMTP server running on ::1:8025")
    loop.run_forever()
    

客户端代码验证

你的客户端代码无需修改,保持原样即可正常发送邮件,服务器会正确打印邮件内容。

内容的提问来源于stack exchange,提问作者LevGor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 19:12:33