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
错误原因
- 缩进错误:服务器代码中,
Controller初始化、事件循环相关代码被错误写在ExampleHandler类内部,属于类属性定义,会在类加载时执行,而非程序运行时的服务器启动逻辑。 - 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
相关产品推荐
相关产品推荐

