之前一直穩(wěn)定運(yùn)行的代碼蔚携,我引進(jìn)新項(xiàng)目的時(shí)候希太,忽然報(bào)錯(cuò)
'Application' object has no attribute 'handlers'"
困擾許久。而且是時(shí)而正常浮梢,時(shí)而出錯(cuò)跛十。
。
無(wú)法忍受秕硝,后開(kāi)啟debug模式排查,發(fā)現(xiàn)原來(lái)是tornado4.5起升級(jí)了,引入了新的tornado.routing模塊远豺。
4.4的Application代碼如下:
class Application(httputil.HTTPServerConnectionDelegate):
"""A collection of request handlers that make up a web application.
``static_handler_class`` setting.
"""
def __init__(self, handlers=None, default_host="", transforms=None,
**settings):
if transforms is None:
self.transforms = []
if settings.get("compress_response") or settings.get("gzip"):
self.transforms.append(GZipContentEncoding)
else:
self.transforms = transforms
self.handlers = []
self.named_handlers = {}
self.default_host = default_host
self.settings = settings
self.ui_modules = {'linkify': _linkify,
'xsrf_form_html': _xsrf_form_html,
'Template': TemplateModule,
}
self.ui_methods = {}
self._load_ui_modules(settings.get("ui_modules", {}))
self._load_ui_methods(settings.get("ui_methods", {}))
if self.settings.get("static_path"):
path = self.settings["static_path"]
handlers = list(handlers or [])
static_url_prefix = settings.get("static_url_prefix",
"/static/")
static_handler_class = settings.get("static_handler_class",
StaticFileHandler)
static_handler_args = settings.get("static_handler_args", {})
static_handler_args['path'] = path
for pattern in [re.escape(static_url_prefix) + r"(.*)",
r"/(favicon\.ico)", r"/(robots\.txt)"]:
handlers.insert(0, (pattern, static_handler_class,
static_handler_args))
if handlers:
self.add_handlers(".*$", handlers)
if self.settings.get('debug'):
self.settings.setdefault('autoreload', True)
self.settings.setdefault('compiled_template_cache', False)
self.settings.setdefault('static_hash_cache', False)
self.settings.setdefault('serve_traceback', True)
# Automatically reload modified modules
if self.settings.get('autoreload'):
from tornado import autoreload
autoreload.start()
到4.5代碼已經(jīng)變成:
class Application(ReversibleRouter):
"""A collection of request handlers that make up a web application.
.. versionchanged:: 4.5
Integration with the new `tornado.routing` module.
"""
def __init__(self, handlers=None, default_host=None, transforms=None,
**settings):
if transforms is None:
self.transforms = []
if settings.get("compress_response") or settings.get("gzip"):
self.transforms.append(GZipContentEncoding)
else:
self.transforms = transforms
self.default_host = default_host
self.settings = settings
self.ui_modules = {'linkify': _linkify,
'xsrf_form_html': _xsrf_form_html,
'Template': TemplateModule,
}
self.ui_methods = {}
self._load_ui_modules(settings.get("ui_modules", {}))
self._load_ui_methods(settings.get("ui_methods", {}))
if self.settings.get("static_path"):
path = self.settings["static_path"]
handlers = list(handlers or [])
static_url_prefix = settings.get("static_url_prefix",
"/static/")
static_handler_class = settings.get("static_handler_class",
StaticFileHandler)
static_handler_args = settings.get("static_handler_args", {})
static_handler_args['path'] = path
for pattern in [re.escape(static_url_prefix) + r"(.*)",
r"/(favicon\.ico)", r"/(robots\.txt)"]:
handlers.insert(0, (pattern, static_handler_class,
static_handler_args))
if self.settings.get('debug'):
self.settings.setdefault('autoreload', True)
self.settings.setdefault('compiled_template_cache', False)
self.settings.setdefault('static_hash_cache', False)
self.settings.setdefault('serve_traceback', True)
self.wildcard_router = _ApplicationRouter(self, handlers)
self.default_router = _ApplicationRouter(self, [
Rule(AnyMatches(), self.wildcard_router)
])
# Automatically reload modified modules
if self.settings.get('autoreload'):
from tornado import autoreload
autoreload.start()
很容易的看到奈偏,Application繼承的父類(lèi)都變了。
新版本的父類(lèi)ReversibleRouter正是新的tornado.routing模塊里面定義的躯护。
我們的項(xiàng)目代碼如下:
class Application(object):
def __init__(self):
from tornado.options import options
self._options = options
self._settings = settings
self._beforecallback = None
self._shutdown_callback = []
self._app = None
def call_shutdown_callback(self):
for callback in self._shutdown_callback:
callback()
def init_settings(self):
from config import FILE_UPLOAD_PATH
import tornado.options
tornado.options.parse_command_line()
self._settings['static_path'] = FILE_UPLOAD_PATH
self._settings['static_url_prefix'] = '/upload/'
self._settings["debug"] = self._options.debug
self._settings['module'] = self._options.module
if not self._settings['module']:
print("the module parameter is required.")
exit(0)
else:
context['module'] = self._settings['module']
if self._settings["debug"]:
self._settings["autoreload"] = True
self.install_autoreload_hook()
if not self._options.debug:
args = sys.argv
args.append("--log_file_prefix=%s" % settings['logfile'])
tornado.options.parse_command_line(args)
@property
def options(self):
return self._options
@property
def handlers(self):
from urls import handlers
return handlers
@property
def settings(self):
return self._settings
def get_app(self):
self._beforecallback(self)
self.init_settings()
self.install_event_hooks()
self.install_middleware()
self.install_rbac()
self.install_message_backend()
from tornado.web import Application
return Application(self.handlers, **self._settings)
本來(lái)計(jì)劃對(duì)著新的代碼修改下我們的代碼惊来, 適配新的版本,不過(guò)發(fā)現(xiàn)變化比較大棺滞。比如這個(gè)Route里面的URLSpec也從tornado.web移動(dòng)到tornado.routing里面了裁蚁,代碼整體變動(dòng)太大,所以還是去修改服務(wù)器的tornado版本吧继准!
class Route(object):
urls = []
def __call__(self, url, name=None):
def _(cls):
if url.startswith("/"):
_url = r"%s" % url
else:
_url = r"/api/%s/%s" % (API_VERSION, url)
self.urls.append(tornado.web.URLSpec(_url, cls, name=name))
return cls
return _
結(jié)論:
雖然問(wèn)題不是代碼層面解決的枉证,但是也是感慨萬(wàn)千。還有就是感覺(jué)運(yùn)氣有點(diǎn)爆棚移必,上周在服務(wù)器一pip install tornado裝的4.4室谚, 隔了一周在服務(wù)器二pip install tornado就裝到4.5了,而且關(guān)鍵變化還挺大崔泵,真是坑懊氤唷!