Bottle与Pywin32协同异常:Windows服务中导入Bottle报错
在Windows服务中导入Bottle框架触发AttributeError的解决方案
嘿,我完全懂你现在的困惑——作为Python新手,想把Bottle跑在Windows服务里,结果光是加个import bottle就炸了,这个问题其实是Windows服务的特性导致的,咱们来一步步解决它。
问题根源
Windows服务运行在没有控制台会话的环境里,默认情况下sys.stdout和sys.stderr是None,而Bottle在导入阶段(就是你写import bottle的那一刻)就会执行这段代码:
_stdout, _stderr = sys.stdout.write, sys.stderr.write
这时候sys.stdout是None,自然就会抛出AttributeError: 'NoneType' object has no attribute 'write'了。
解决方案
咱们有两种靠谱的方法来解决这个问题:
方法1:导入Bottle前重定向标准流
在导入Bottle之前,把sys.stdout和sys.stderr重定向到一个日志文件或者空设备(os.devnull),给Bottle提供一个有效的write对象:
import sys import os # 可选:重定向到日志文件,方便后续调试 log_file_path = os.path.join(os.path.dirname(__file__), "bottle_service_log.txt") sys.stdout = open(log_file_path, "a", encoding="utf-8") sys.stderr = open(log_file_path, "a", encoding="utf-8") # 或者如果不需要日志,重定向到空设备 # sys.stdout = open(os.devnull, "w") # sys.stderr = open(os.devnull, "w") # 现在再导入Bottle就不会报错了 import bottle import win32serviceutil import win32service import win32event import servicemanager import logging import threading class AppServerSvc(win32serviceutil.ServiceFramework): _svc_name_ = 'PestService' _svc_display_name_ = 'The Pest Service' _svc_description_ = "This service writes stuff to a file" def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) def SvcStop(self): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) win32event.SetEvent(self.hWaitStop) def SvcDoRun(self): self.ReportServiceStatus(win32service.SERVICE_RUNNING) win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE) self.main() def main(self): pass if __name__ == '__main__': win32serviceutil.HandleCommandLine(AppServerSvc)
方法2:延迟导入Bottle
不在模块顶层导入Bottle,而是等到服务启动后、已经处理好标准流的时机再导入,比如在SvcDoRun方法里:
import win32serviceutil import win32service import win32event import servicemanager import logging import threading import sys import os class AppServerSvc(win32serviceutil.ServiceFramework): _svc_name_ = 'PestService' _svc_display_name_ = 'The Pest Service' _svc_description_ = "This service writes stuff to a file" def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) def SvcStop(self): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) win32event.SetEvent(self.hWaitStop) def SvcDoRun(self): # 先处理标准流 log_file_path = os.path.join(os.path.dirname(__file__), "bottle_service_log.txt") sys.stdout = open(log_file_path, "a", encoding="utf-8") sys.stderr = open(log_file_path, "a", encoding="utf-8") # 延迟导入Bottle import bottle self.ReportServiceStatus(win32service.SERVICE_RUNNING) win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE) self.main() def main(self): pass if __name__ == '__main__': win32serviceutil.HandleCommandLine(AppServerSvc)
额外提示
如果你需要更专业的日志管理,建议使用Python的logging模块来替代直接重定向stdout/stderr,这样可以更好地控制日志级别、格式和存储方式,不过核心思路还是要保证Bottle导入时sys.stdout和sys.stderr不是None。
内容的提问来源于stack exchange,提问作者ramires.cabral
相关产品推荐
相关产品推荐

