【问题标题】:Change blueprints or Reload flask app during runtime在运行时更改蓝图或重新加载烧瓶应用程序
【发布时间】:2013-04-07 14:51:54
【问题描述】:

我正在编写一个支持插件架构的 Flask 应用程序。每个插件都位于一个单独的文件夹中,并且是一个模块,它至少具有一个类,该类是 Plugin 类的子类。出于安全原因,我不想在烧瓶应用程序最初运行时加载所有插件。相反,用户可以从烧瓶应用程序中启用插件。一旦他这样做了,我们就会在数据库中存储一个备忘录,将应用程序列入白名单以供加载。但是,我们仍然必须记住哪些插件被禁用并证明了这些插件的视图。我通过为未启用且不加载任何自定义代码的插件创建一个虚拟类来做到这一点。

每个插件都有自己的蓝图。我们在加载插件时注册它。蓝图还定义了启用插件的路线。 整个过程是这样的:

for plugin_name in os.listdir(plugin_dir):
    plugin_path = os.path.join(plugin_paths, plugin_name)
    module_name = "plugins.{}.__init__".format(plugin_name)
    plugin_enabled = ask_db_whether_plugin_is_enabled(plugin_name)

    if os.path.isdir(plugin_path) and plugin_enabled:
        module = __import__(module_name)
        for plugin in load_plugins_from_module(module):
            app.register_blueprint(plugin.blueprint, url_prefix='/plugins')
    else:
        PluginCls = type(identifier, (Plugin, ), {})
        disabled_plugin = PluginCls()
        app.register_blueprint(disabled_plugin.blueprint, url_prefix='/plugins')

load_plugins_from_module 看起来像这样:

def load_plugins_from_module(module):
    def is_plugin(c):
        return inspect.isclass(c) and \
               issubclass(c, Plugin) and \
               c != Plugin

    for name, objects in inspect.getmembers(module, lambda c: inspect.ismodule(c)):
        for name, PluginCls in inspect.getmembers(objects, is_plugin):
            plugin = PluginCls()
            yield plugin

现在的问题是:当我将插件更改为启用时,我基本上想重新运行

module = __import__(module_name)
for plugin in load_plugins_from_module(module):
    app.register_blueprint(plugin.blueprint, url_prefix='/plugins')

用于该插件的模块,使其变为活动状态并注册所有已在子类插件中定义的路由。这将引发AssertionError,因为我无法在运行时更改蓝图。什么是一个好的解决方法?我可以从应用程序内重新加载应用程序吗?我可以在运行时修改现有的蓝图吗?

感谢您的帮助!

【问题讨论】:

    标签: python flask


    【解决方案1】:

    我不确定如何在运行时修改应用程序对象,但您可以尝试另一种方法来满足您的需要。

    • 创建一个名为“Plugins”的表,其中包含 2 列:

      Field 1: Blueprint name
      
      Field 2: isActive
      
    • 为用户提供一个界面,让他们可以“激活”所需的插件。您可以根据需要控制对它的访问。所以你可以写一个视图如:

      @login_required
      def activate_plugin(name):
      
      #whatever code is needed to activate the flag in Plugins table.
      
    • 蓝图可以有一个 before_request() 方法,您可以使用该方法检查插件/蓝图是否已启用。如果不启用,返回 404 或任何代码。

      @blueprintname.before_request
      def check_if_active(blueprintname):
      
          is_active = some_function_that_checks_plugin_active_flag()
          if is_active:
            #Normal processing
          else:
              abort(404)
      
    • 这样,对蓝图视图的每个请求都会在返回响应之前首先检查它是否处于活动状态。

    【讨论】:

    • 感谢您的回答。问题是插件中的代码仍然必须执行 - 很容易有一个不在路由后面的 drop db,而是在加载模块时执行。
    【解决方案2】:

    我不确定您是否需要让这个复杂化。

    您可以简单地为要启用的插件设置配置选项。您可以在“Start_app()”方法中根据该配置注册您的蓝图。

    您还可以动态设置从某些文件夹/文件继承的配置选项,例如使其更具动态性。

    插件通常由开发人员提供,因此配置选项并不繁琐,除非您尝试构建一个让每个随机用户都可以修改您的网站的东西——这可能会带来巨大的安全问题。

    出于安全原因,我不想在 最初运行烧瓶应用程序。

    对此我不确定。不允许用户手动启动插件,这会带来更多的安全风险(因此,如果用户能够秘密上传代码,然后他现在可以启用它们)。

    您可以制作一个像 WordPress 一样支持插件的 CMS,只需在用户点击“激活插件”之前不路由插件的网址

    【讨论】:

    • 谢谢。你可能是对的,毕竟在运行时激活东西可能没有必要。
    猜你喜欢
    • 2019-06-22
    • 2017-12-18
    • 2020-10-26
    • 2021-01-10
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 2012-03-29
    • 2019-07-05
    相关资源
    最近更新 更多