Merge commit '47296980495f8bbfc9493e93de85dd62de6fa6b9' as 'paste-framework'
This commit is contained in:
Executable
+236
@@ -0,0 +1,236 @@
|
||||
import importlib
|
||||
import pkgutil
|
||||
from types import ModuleType
|
||||
from typing import Optional, Any
|
||||
|
||||
import tornado
|
||||
from tornado.routing import URLSpec
|
||||
from tornado.web import OutputTransform, _RuleList
|
||||
|
||||
|
||||
class Application(tornado.web.Application):
|
||||
"""
|
||||
从 Tornado 派生的应用程序类。
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def modules_iterator(cls, package: [str, ModuleType]):
|
||||
"""
|
||||
从 package 包装载所有模块。这里返回模块迭代器。
|
||||
若为字符串,则直接从目录中载入模块;若为模块,则根据模块的参数装载。
|
||||
|
||||
:param package: 包,允许为路径或包(模块对象)
|
||||
:return: 模块迭代器
|
||||
"""
|
||||
if isinstance(package, str):
|
||||
package = importlib.import_module(package)
|
||||
|
||||
# 模块迭代器,能够遍历出所有子包中的子模块
|
||||
return pkgutil.walk_packages(package.__path__, f"{package.__name__}.")
|
||||
|
||||
@classmethod
|
||||
def fetch_handlers(cls, module: ModuleType):
|
||||
"""
|
||||
查找模块中所有的请求处理类,即类 RequestHandler 的子类。
|
||||
|
||||
:param module: 模块
|
||||
:return: [(路由模式, 请求处理类)]
|
||||
"""
|
||||
# 判断是否是有效的 RequestHandler 类,且是 RequestHandler 的子类
|
||||
def is_handler(handler_cls):
|
||||
return isinstance(handler_cls, type) and issubclass(handler_cls, tornado.web.RequestHandler)
|
||||
|
||||
# 判断是否拥有 route_pattern 模式属性,且该属性值为字符串类型
|
||||
def has_pattern(handler_cls):
|
||||
return hasattr(handler_cls, 'route_pattern') and isinstance(getattr(handler_cls, 'route_pattern'), str)
|
||||
|
||||
handlers: list[tuple[str, ModuleType]] = []
|
||||
# 迭代模块成员
|
||||
for _n in dir(module):
|
||||
_cls = getattr(module, _n)
|
||||
is_hdl = is_handler(_cls)
|
||||
has_pat = has_pattern(_cls)
|
||||
if is_hdl and has_pat:
|
||||
_route = _cls.route_pattern
|
||||
handlers.append((_route, _cls))
|
||||
|
||||
return handlers
|
||||
|
||||
@classmethod
|
||||
def load_ui_modules(cls, ui_modules_config):
|
||||
"""
|
||||
将JSON配置中的模块字符串转换为实际的类。
|
||||
"""
|
||||
loaded_modules = {}
|
||||
for name, path in ui_modules_config.items():
|
||||
try:
|
||||
module_path, class_name = path.rsplit('.', 1)
|
||||
module = importlib.import_module(module_path)
|
||||
loaded_modules[name] = getattr(module, class_name)
|
||||
except (ImportError, AttributeError) as e:
|
||||
raise RuntimeError(f"Failed to load UIModule {name} from {path}: {str(e)}")
|
||||
return loaded_modules
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
handlers: Optional[_RuleList] = None,
|
||||
handlers_pkg: [str, ModuleType] = None,
|
||||
uri_prefix: str = "",
|
||||
**settings: Any
|
||||
) -> None:
|
||||
"""
|
||||
重写应用程序构造函数,增加自动装载功能。
|
||||
|
||||
:param handlers: 请求处理路由配置列表
|
||||
:param handlers_pkg: 执行自动装载的请求处理类所在包
|
||||
:param uri_prefix: URI 前缀
|
||||
:param settings: 其他配置
|
||||
"""
|
||||
|
||||
self.routes: list[(URLSpec, _RuleList)] = []
|
||||
"""
|
||||
请求处理路由列表。
|
||||
"""
|
||||
|
||||
if uri_prefix:
|
||||
uri_prefix = uri_prefix if uri_prefix.startswith('/') else f"/{uri_prefix}"
|
||||
|
||||
self.uri_prefix = uri_prefix
|
||||
"""
|
||||
统一资源标识符前缀。仅支持动态加载的请求处理类。
|
||||
"""
|
||||
|
||||
# 合并构造参数中的请求处理路由
|
||||
if handlers:
|
||||
self.routes.extend(handlers)
|
||||
|
||||
# 动态加载请求处理类,并执行合并
|
||||
if handlers_pkg:
|
||||
self.routes.extend(self.load_handlers(handlers_pkg=handlers_pkg))
|
||||
|
||||
self.before_create()
|
||||
|
||||
super().__init__(handlers=self.routes, **settings)
|
||||
|
||||
def before_create(self):
|
||||
"""
|
||||
在创建应用之前执行。
|
||||
"""
|
||||
pass
|
||||
|
||||
def load_handlers(self, handlers_pkg: [str, ModuleType] = None):
|
||||
"""
|
||||
从 handlers_pkg 指定的包装载所有模块,分析出所有请求处理类和路由路径,并返回。
|
||||
|
||||
:param handlers_pkg: 模块根目录,允许为路径或包(模块对象)
|
||||
:return: 动态装载的所有路由配置
|
||||
"""
|
||||
_routes = []
|
||||
|
||||
if handlers_pkg is None:
|
||||
return _routes
|
||||
|
||||
# 迭代器装载所有子包中的子模块
|
||||
modules_itr = self.modules_iterator(package=handlers_pkg)
|
||||
for _file_finder, _module_name, _is_package in modules_itr:
|
||||
if _is_package:
|
||||
continue
|
||||
|
||||
_module = importlib.import_module(_module_name)
|
||||
_handlers = self.fetch_handlers(module=_module)
|
||||
for _hdl in _handlers:
|
||||
_pattern, _cls = str(_hdl[0]), _hdl[1]
|
||||
_pattern = _pattern if _pattern.startswith('/') else f"/{_pattern}"
|
||||
_url_spec = tornado.web.url(
|
||||
pattern=f"{self.uri_prefix}{_pattern}", handler=_cls, name=_cls.__name__
|
||||
)
|
||||
_routes.append(_url_spec)
|
||||
|
||||
return _routes
|
||||
|
||||
|
||||
class ApplicationSwagger(Application):
|
||||
"""
|
||||
从框架 Application 派生,增加对 Swagger 的支持。
|
||||
"""
|
||||
|
||||
swagger_schema = ""
|
||||
"""
|
||||
在 Swagger 注入时,保存 json schema。
|
||||
"""
|
||||
|
||||
swagger_home_template = ""
|
||||
"""
|
||||
在 Swagger 注入时,保存 Ui 页面内容。
|
||||
"""
|
||||
|
||||
swagger_url = "/docs"
|
||||
"""
|
||||
Swagger URL。
|
||||
"""
|
||||
|
||||
swagger_api_base_url = "/"
|
||||
"""
|
||||
Swagger API base URL。
|
||||
"""
|
||||
|
||||
swagger_title = ""
|
||||
"""
|
||||
Swagger 页面标题。
|
||||
"""
|
||||
|
||||
swagger_description = ""
|
||||
"""
|
||||
Swagger 页面描述。
|
||||
"""
|
||||
|
||||
swagger_api_version = ""
|
||||
"""
|
||||
Swagger 页面版本。
|
||||
"""
|
||||
|
||||
swagger_contact = ""
|
||||
"""
|
||||
Swagger 页面联系方式。
|
||||
"""
|
||||
|
||||
swagger_schemes = ["http", "https"]
|
||||
"""
|
||||
Swagger 协议方案。
|
||||
"""
|
||||
|
||||
def __init__(self, **settings: Any) -> None:
|
||||
self.swagger_schema = settings.get('swagger_schema', self.swagger_schema)
|
||||
self.swagger_home_template = settings.get('swagger_home_template', self.swagger_home_template)
|
||||
|
||||
self.swagger_url = settings.get('swagger_url', self.swagger_url)
|
||||
self.swagger_url = self.swagger_url if self.swagger_url.startswith('/') else f"/{self.swagger_url}"
|
||||
|
||||
self.swagger_api_base_url = settings.get('swagger_api_base_url', self.swagger_api_base_url)
|
||||
self.swagger_api_base_url = self.swagger_api_base_url if self.swagger_api_base_url.startswith('/') \
|
||||
else f"/{self.swagger_api_base_url}"
|
||||
|
||||
self.swagger_title = settings.get('swagger_title', self.swagger_title)
|
||||
self.swagger_description = settings.get('swagger_description', self.swagger_description)
|
||||
self.swagger_api_version = settings.get('swagger_api_version', self.swagger_api_version)
|
||||
self.swagger_contact = settings.get('swagger_contact', self.swagger_contact)
|
||||
self.swagger_schemes = settings.get('swagger_schemes', self.swagger_schemes)
|
||||
|
||||
super().__init__(**settings)
|
||||
|
||||
def before_create(self):
|
||||
_swagger_url = f"{self.uri_prefix}{self.swagger_url}"
|
||||
_swagger_api_base_url = f"{self.uri_prefix}{self.swagger_api_base_url}"
|
||||
|
||||
from paste.web.swagger import setup_swagger
|
||||
setup_swagger(
|
||||
app=self,
|
||||
routes=self.routes,
|
||||
swagger_url=_swagger_url,
|
||||
api_base_url=_swagger_api_base_url,
|
||||
title=self.swagger_title,
|
||||
description=self.swagger_description,
|
||||
api_version=self.swagger_api_version,
|
||||
contact=self.swagger_contact,
|
||||
schemes=self.swagger_schemes,
|
||||
)
|
||||
Reference in New Issue
Block a user