Flask-PluginKit

一个基于Flask的插件开发工具,支持多种扩展类型,您可以用来创建插件而无需更改核心代码。

插件可以是本地目录,也可以是第三方包(比如pypi)。

同时,这里有一个插件组:https://github.com/flask-pluginkit

https://codecov.io/gh/staugur/Flask-PluginKit/branch/master/graph/badge.svg https://img.shields.io/pypi/v/Flask-PluginKit.svg?style=popout https://img.shields.io/pypi/pyversions/Flask-PluginKit.svg

快速开始

安装使用

先安装(Installation),再初始化(Initialization)

$ pip install -U Flask-PluginKit

第一种方法:普通模式(Usage)

from flask_pluginkit import PluginManager, Flask
app = Flask(__name__)
plugin = PluginManager(app)

第二种方法:工厂模式(The factory pattern)

from flask_pluginkit import PluginManager, Flask
app = Flask(__name__)
plugin = PluginManager()
plugin.init_app(app)

插件结构

安装完成后,您可以开发第一个插件。最小的插件需要至少拥有自己的目录,目录必须包含 __init__.py 文件,否则不认为这是一个插件包!

您的插件核心代码应该写在 __init__.py 文件中,包含注册插件所需的元数据。

一个最简单的插件可能是这样的:

plugins/example/
├── __init__.py

一个复杂的插件也可能是这样的:

plugins/example/
├── __init__.py
├── static
│   └── example
│       └── demo.css
└── templates
    └── example
        └── demo.html

Hello World!

这是一个示例应用,代码 在这里

这个示例中的演示插件(位于plugins/example),它基本上已经包含了Flask-PluginKit目前所提供的所有功能点,它本来就是一个帮助插件,可以复制修改一份建立属于您的插件。

  • 安装依赖包, pip install -r test-requirements.txt #包含了一个第三方插件和一个本地插件

  • 运行它, sh run.sh ,首页大概是这样:

    image0

    温馨提示:红色部分,就是第三方插件中的css设定的,支持插件添加静态文件,在v0.1.8及之后版本支持通过register_yep注册静态文件,当然也可以直接在模板中link css,但不建议。

  • 更多插件相关,请参阅 插件详解 一节。

启用、禁用插件

无论是本地插件还是第三方插件,都应该安装在了本地环境中,启用(enabled)、禁用(disabled)一个插件有两种方法。

  • 第一种方法是将__init__.py__state__值设为enableddisabled

  • 第二种方法是添加ENABLEDDISABLED文件(此方法优先级高于第一种,DISABLED文件优先级高于ENABLED文件)

    请注意:启用、禁用的操作需要重启Web应用程序才能生效,需要您自行解决,使用gunicorn作为启动的,可以参考 blueprint 中的路由函数,原理是通过向主进程发送HUP信号重载app。

插件详解

插件概述

插件可以是一个本地目录,它应该位于程序plugins目录下,且是一个合法的python包,即:plugins下包含 __init__.py 文件,插件也包含 __init__.py , 这个文件标识了目录是一个包,同时也标识了这个插件,是核心代码,插件入口。

但请注意:插件亦可以是一个第三方包,您可以使用pip安装它,而不必放到程序当前目录!第三方插件的格式也应该同本地目录一般,详细见 #第三方插件

术语表

  • tep - 模板扩展点
  • hep - 钩子扩展点
  • bep - 蓝图扩展点
  • yep - 样式扩展点
  • dcp - 动态连接点
  • dfp - 动态函数扩展点

核心代码

这里是一个最迷你的代码:

# -*- coding: utf-8 -*-

#: 你的插件名称,不严格要求和插件目录名称保持一致.
#: Your plugin name is not strictly required to be consistent with the plugin directory name.
__plugin_name__ = "Demo"

#: 插件描述信息,什么用处.
#: What is the use of plug-in description information.
__description__ = "A Plugin Demo"

#: 插件作者
#: Plugin Author
__author__ = "Mr.tao <staugur@saintic.com>"

#: 插件版本
#: Plugin Version
__version__ = "0.1.1"

#: 返回插件主类
#: (eturns the plugin main class.
def getPluginClass():
    return PluginDemoMain

#: 插件主类, 请保证getPluginClass准确返回此类
#: (The plugin main class, please ensure that getPluginClass returns this class exactly.)
class PluginDemoMain(object):

    def run(self):
        pass

这里还有一个完整的例子,包含了Flask-PluginKit的完整功能,点击查看

代码解析

参考上面迷你代码,这是一个插件所需要的最少的代码,包含元数据(__meta__)和插件类(由getPluginClass函数返回)。

元数据(__meta__)完整列表:

#: 你的插件名称,不严格要求和插件目录名称保持一致.
#: Your plugin name is not strictly required to be consistent with the plugin directory name.
__plugin_name__ = "Demo"

#: 插件描述信息,什么用处.
#: What is the use of plug-in description information.
__description__ = "A Plugin Demo"

#: 插件作者
#: Plugin Author
__author__ = "Mr.tao <staugur@saintic.com>"

#: 插件版本
#: Plugin Version
__version__ = "0.1.1"

#: 插件主页
#: Plugin Homepage
__url__ = "https://github.com/staugur/Flask-PluginKit"

#: 插件许可证
#: Plugin LICENSE
__license__ = "MIT"

#: 插件许可证文件,你的插件目录下应该有个名叫LICENSE的许可证文件
#: The plugin LICENSE file. Your plugin directory should have a LICENSE file called LICENSE.
__license_file__= "LICENSE"

#: 插件自述文件,你的插件目录下应该有个README的描述说明文件
#: The plugin profile should have a README description file in your plugin directory.
__readme_file__ = "README"

#: 插件状态, enabled、disabled
#: The plugin Status, enabled or disabled.
__state__ = "enabled"

插件类:

def getPluginClass():
    return YourPluginClass

class YourPluginClass(object):

    def run(self):
        pass

    def register_tep(self):
        """注册模板入口, 返回扩展点名称及扩展的代码, 其中include点必须是实际的HTML文件, string点是HTML代码、字符串等."""
        return dict()

    def register_hep(self):
        """注册钩子上下文入口, 返回扩展点名称及执行的函数"""
        return dict()

    def register_bep(self):
        """注册蓝图入口, 返回蓝图路由前缀及蓝图名称"""
        return dict()

    def register_yep(self):
        """注册样式扩展点,返回扩展点及对应css文件"""
        return dict()

    def register_dfp(self):
        """注册函数扩展点,返回扩展点名称及对应函数,实际使用push_func方法"""
        return dict()

插件类详解

插件类可以是继承自程序的某个基类,run、register_*至少存在一个方能加载为插件;如果你想使用程序其他基类接口,可能需要在 __init__.py 顶处导入:

#: 若想引用程序基类需先导入这个模块
#: If you want to refer to the program base class, you need to pilot the module.
from __future__ import absolute_import

方法: run -> 仅插件加载时运行此方法

环境: 非web环境

用法: 普通方法

方法: register_tep -> 注册模板扩展点,提供模板文件或HTML代码

环境: web请求上下文、模板中使用

用法:
  • 要求返回字典,格式是: dict(扩展点=HTML字符串或模板文件)
  • 以.html .htm结尾即模板文件,模板文件应该在”插件包/templates”下
  • 非模板文件支持解析HTML代码,不支持jinja2过滤器、函数等
  • 建议您在插件templates下新建目录存放html文件,因为flask-pluginkit仅加载插件下templates目录,且不保证模板冲突,新建目录可以避免与其他插件模板文件冲突,导致无法正常引用。
  • 支持模板排序,您需要初始化 PluginManager 时传入 stpl=True 即可支持。register_tep时,格式是:排序数字@模板代码或文件

示例-注册:

# 插件类中
def register_tep(self):
    return dict(base_header="example/header.html", base_footer="Copyright 2018.")

# 如上,您需要在插件目录下新建"templates/example"目录,并将header.html放入目录中,若不存在会引发 ``flask_pluginkit.exceptions.PluginError`` 异常。

示例-使用:

# 使用模板扩展点需要在HTML中渲染或在蓝图中通过 ``render_template`` 返回响应。

# 模板中。假设以下文件名为base.html是基础模板(插件目录/templates/example/base.html),通过 ``emit_tep`` 引用,可以传入额外数据

<html>
<head>
    {{ emit_tep("base_header") }}
</head>
<body>
    {{ emit_tep("base_footer", extra=dict(a=1, b=True, c=[])) }}
</body>
</html>

## PS: 亦可在其他模板中继承此base.html模板, {% extends "example/base.html" %}, 切记对于模板来说根目录是"插件下/templates"目录,所以强烈建议在此目录下新建子目录。

# 蓝图中。
from flask import Blueprint, render_template

plugin_blueprint = Blueprint("example", "example")
# 同 plugin_blueprint = Blueprint("example", "example", template_folder="templates")

@plugin_blueprint.route("/")
def plugin():
    return render_template("example/base.html")

方法: register_hep -> 注册钩子扩展点,在flask钩子中注册函数

环境: web请求上下文、注册到flask钩子

用法:
  • 要求返回字典,格式是: dict(扩展点=function),目前支持三种扩展点: before_request_top_hook、before_request_hook、after_request_hook、teardown_request_hook
  • 三种扩展点对应的钩子分别是请求前(优先级为1)、请求前、请求后(返回前)、请求后(返回前,无论是否发生异常)
  • before_reqest_hook还可以拦截请求,要求使用了make_response、jsonify等响应函数或Response构造响应类,且设置属性is_before_request_return=True
  • 建议您在插件类中单独写一个方法,并传递给扩展点,其中after_request_hook会传入 response 参数,teardown_request_hook会传入 exception 参数,您扩展点的函数必须支持传入,并可以自行使用。

示例:

from flask import request, g

# 插件类中
def set_login(self):
    g.login_in = request.args.get("username") == "admin"

def register_hep(self):
    return {"after_request_hook": lambda resp: resp, "before_request_hook": self.set_login}

# 如上,您的程序在运行后,每次请求前都会执行before_request_hook的self.set_login函数,请求后返回前会执行after_request_hook的匿名函数。

方法: register_bep -> 注册蓝图扩展点,用来注册一个蓝图

环境: web请求上下文

用法: 注册蓝图,要求返回字典,dict(blueprint=蓝图类, prefix=蓝图挂载点(比如/example))

示例:

from flask import Blueprint

plugin_blueprint = Blueprint("example", "example")

# 插件类中
def register_bep(self):
    return dict(blueprint=plugin_blueprint, prefix="/example")

# 如上,您的程序将会多一个蓝图,URL路径是/example。

方法: register_yep -> 注册静态扩展点,提供模板所需引入的css样式

环境: web请求上下文、模板中使用

用法: 要求返回字典,类似于register_tep,格式是: dict(扩展点=CSS文件),CSS文件应该在”插件包/static”目录下。

示例-注册:

# 插件类中
def register_yep(self):
    return {"base": "example/demo.css"}

# 如上,您的插件目录下应该创建"static/example"目录,并将demo.css放入其中,若不存在同样会引发 ``flask_pluginkit.exceptions.PluginError`` 异常。

示例-使用:

# 同注册模板上下文的使用方法,使用 ``emit_yep`` 渲染。

<html>
<head>
    {{ emit_yep("base") }}
</head>
<body>
    代码
</body
</html>

方法: register_dfp -> 注册函数扩展点,仅插件加载时运行此方法

环境: 非web环境

用法: 普通方法

示例-注册:

# 插件类中
def register_dfp(self):
    return dict(test_func=lambda:"test a func")

示例-使用emit_func方法调用,参考 动态函数扩展点

配置信息(config)

您可以在初始化 PluginManager 时传入pluginkit_config参数设置额外的配置(以供插件使用),另外,此参数会加载app.config中 PLUGINKIT_ 开头的配置。

在插件中,可以使用 flask_pluginkit.PluginManager.get_config 属性获取配置信息。

在应用上下文中,可以使用 flask_pluginkit.emit_config 获取配置信息。

简单存储(s3)

v1.3.0支持简单存储服务,其配置姑且命名s3,初始化 PluginManager 时传递s3,值为local(本地文件)、redis(需要传递s3_redis参数,即redis_url),目前仅支持这两种。 不过您也可以自定义存储类,要求是继承自 BaseStorage, 执行 storage 函数时传入 sf(继承的类)args(继承类参数,如果有的话)

使用简单存储有两种情况,一是在app应用上下文及请求上下文中,二是在程序中独立使用:

# 第一种情况, web环境中, PluginManager加载插件类中集成了 `storage` 方法,附加到app.extensions['pluginkit']中,调用它使用以下:

from flask import current_app
current_app.extensions['pluginkit'].storage()

# 第二种情况

from flask_pluginkit import LocalStorage

# 插件类中
def run(self):
    self.s3 = LocalStorage()

# 两者使用LocalStorage类,由于存储在本地同个文件中,所以数据时相同的;
# 使用RedisStorage类,连接配置的库不同,数据会相应改变,同库则数据相同。

动态连接点(dcp)

动态连接点,动态注册并执行函数将结果返回给模板使用。您可以在上下文中通过 flask_pluginkit.push_dcp() 推送给标识点一个函数,在模板中通过 emit_dcp 执行并获取执行结果(即HTML代码)。

用法:

``emit_dcp`` 可以像 ``emit_tep`` 一样传入额外数据(context),并且在函数中调用。

``push_dcp`` 传入标识点、函数和定位,需要在请求上下文中执行::
    event: 标识点,有效字符串
    callback: 普通函数、匿名函数、类、类实例化的方法
    position: 定位,默认right插入event末尾,left插入event首位,在 ``emit_dcp`` 中可以体验输出效果

请注意: 每次使用 ``emit_dcp`` 后都会清空此标识点的函数!

使用案例:

from flask import render_template

from flask_pluginkit import Flask, PluginManager, push_dcp

app = Flask(__name__)

PluginManager(app)

def test(extra):
    return extra + 'test'

@app.route("/")
def index():
    push_dcp("event", test, "left")
    return render_template("index.html")

# index.html
{{ emit_dcp('event', extra='template') }}

动态函数扩展点(dfp)

v2.3.0增加,此功能可以让插件或用户动态地推送一个可回调函数或类方法。

您可以在上下文中通过 flask_pluginkit.PluginManager.push_func() 推送给标识点一个函数、类、类方法等, 通过 flask_pluginkit.PluginManager.emit_func() 方法执行并获取执行结果。

用法:

`emit_func` 调用时就像是调用一般函数、类、类方法一样,支持传入可变长度参数和可变字典参数以适应函数调用。

`push_func` 传入标识名、可回调函数或类方法,调用要求请参考具体插件说明(也许要求在应用上下文、也许要求在请求上下文)::
    cuin: 标识名称,有效字符串
    callback: 普通函数、匿名函数、类、类实例化的方法

使用案例:

from flask_pluginkit import Flask, PluginManager

app = Flask(__name__)

plugin = PluginManager(app)

def test(extra, key='value'):
    return extra + 'test; key value: ' + key

plugin.push_func("test", test)

@app.route("/")
def index():
    test_result = plugin.emit_func("test", "this is extra text", key='diy_key')
    return test_result

加载逻辑

插件加载在程序启动时完成, 加载类是 PluginManager, 它的析构函数支持你传递plugins_base(默认程序目录)、plugins_folder(插件所在目录)设置插件绝对路径目录,还支持工厂模式,更多参数参见API文档。

流程如下:

  1. 通过 init_app 完成实例构造,初始化参数。
  2. 扫描插件目录,符合插件规则的包将被动态加载。
  3. 加载插件信息,依次运行 run -> register_tep -> register_hep -> register_bep -> register_yep -> register_dfp 等方法, 写入到所有插件列表。
  4. Flask-PluginKit设置支持多模板文件夹、多静态文件夹(插件目录下)。
  5. Flask-PluginKit注册全局模板函数 emit_tepemit_yepemit_dcp, 分别是渲染模板上下文、CSS上下文、渲染动态连接点。
  6. 注册所有启用插件的蓝图扩展点BEP。
  7. 使用before_request等装饰器注册所有启用插件的钩子扩展点。
  8. PluginManager 附加到app中,完成加载,可以使用 app.extensions['pluginkit']app.plugin_manager 调用 PluginManager 类中方法。

Third party plugin

第三方插件是指非程序子目录、来自于pip或easy_install等安装的本地模块。

第三方插件解放使用,程序可以不用将插件代码放到子目录,只需要使用 pip install 安装到本地机器上,然后在 PluginManager 初始化时传入 plugin_packages 参数即可。

这意味着,任何人都可以编写一个包,发布到pypi;使用者写好 requirements.txt 并安装依赖,在初始化中调用,一气呵成,而几乎不用担心后续第三方插件升级。

调用:

from flask_pluginkit import Flask, PluginManager
app = Flask(__name__)
plugin_manager = PluginManager(app, plugin_packages=["flask_pluginkit_demo"])

如何编写第三方插件:

  1. 首先根据上方 代码解析插件类详解 写一个包,参见 核心代码 ,要写在 __init__.py 中。

  2. 第一步中实际上就是编写本地插件的过程,本步骤需要编写 setup.py ,使本地插件能(可选发布到pypi中)通过pip供其他人使用:

    from setuptools import setup
    setup(
        name='flask_pluginkit_demo',
        packages=['flask_pluginkit_demo',],
        include_package_data=True,
       ...
    )
    
  3. 如果你的插件包含模板目录templates或静态目录static等,需要再编写一个额外的清单文件 MANIFEST.in:

    recursive-include flask_pluginkit_demo/templates *
    recursive-include flask_pluginkit_demo/static *
    
  4. 测试发布

    4.1 打包:python setup.py sdist bdist_wheel #更多参数自行调整

    4.2 到这里,可以使用 pip install . 在本地测试是否正常安装。

    4.3 本地测试通过,可以发布到test.pypi.org,这是官方pypi的测试站,里面的包不会被轻易使用,命令是: twine upload –repository-url https://test.pypi.org/legacy/ dist/*

    4.4 测试站可以看看界面描述等等是否符合心中要求,没问题就发布到正式站,pypi.org,命令是: twine upload dist/*

  5. 示例

    pypi demo

插件周边

fixflask.py

这个文件是继承Flask的类,增加了一些功能,即支持多静态文件夹(来自于 flask-multistatic )、支持 before_request_top (此装饰器与before_request作用一致,区别在于它总是将装饰的函数置于钩子首位)和 before_request_second (同上,但是这个装饰器总是把函数置于第二位)。

使用方法:

from flask_pluginkit import Flask
app = Flask(__name__)
@app.before_request_top
def first():
    pass
@app.before_request_second
def second():
    pass

web.py

这是一个蓝图,blueprint, 在您的app中注册此蓝图,它提供一个页面展示插件列表,支持禁用、启用插件、重启app、访问认证。

当然,这个蓝图仅支持flask-pluginkit的 PluginManager 初始化的app,没有此扩展,蓝图不可用。

重启app功能:

# 支持生产环境重启gunicorn、uwsgi

# 要求安装模块
pip install psutil

# 使用gunicorn启动的应用 - app配置
app.config.update(ENV="production", PLUGINKIT_GUNICORN_ENABLED=True, PLUGINKIT_GUNICORN_PROCESSNAME="进程名")

# 使用uwsgi启动的应用 - app配置
app.config.update(ENV="production", PLUGINKIT_UWSGI_ENABLED=True)

访问认证:

# 支持字段认证、HTTP Basic Auth

# app配置
app.config.update(PLUGINKIT_AUTHMETHOD=值)

值为BOOL为字段认证,使用 ``g.signin`` 认证,当此值为 ``True`` 时允许访问;您也可以使用 ``PLUGINKIT_AUTHFIELD`` 配置项指定,当此项存在且为True时允许访问。

值为BASIC为HTTP Basic Auth,支持传入 ``PLUGINKIT_AUTHREALM`` 设置提示信息,必须传入 ``PLUGINKIT_AUTHUSERS`` 设置认证的用户名及密码,要求类型是字典,key是用户名,value是密码,允许多个k、v组合。

示例:

from flask_pluginkit import Flask, PluginManager, blueprint

app = Flask(__name__)

app.update(ENV="production", PLUGINKIT_GUNICORN_ENABLED=True, PLUGINKIT_GUNICORN_PROCESSNAME="gunicorn: master [xxx]", PLUGINKIT_AUTHREALM="BASIC", PLUGINKIT_AUTHUSERS=dict(admin="admin"))

plugin = PluginManager(app)

app.register_blueprint(blueprint, url_prefix="/PluginManager")

installer.py

安装插件类, PluginInstaller, 支持添加http远程插件和本地插件,提供 addPluginremovePlugin 两个api接口(返回dict,参数见Api Document)

Api Documentation

Restful api returns the json format

  • Return json data, public return: {“msg”: null, “code”: int}
  • When code is 0, the request is successful; non-zero is the request failure, at this time, msg has a failure message.

flask_pluginkit

Flask-PluginKit.flask_pluginkit

pluginkit: load and run plugins.

copyright:
  1. 2018 by staugur.
license:

BSD 3-Clause, see LICENSE for more details.

class flask_pluginkit.flask_pluginkit.PluginManager(app=None, plugins_base=None, plugins_folder='plugins', **kwargs)[源代码]

基类:object

Flask Plugin Manager Extension, collects all plugins and maps the metadata to the plugin.

The plugin is a directory, the directory name is the plugin name, and the plugin entry file is __init__.py, including __plugin_name__, __description__, __version__, __author__, __state__ and other plugin metadata.

A meaningful plugin structure should look like this:

plugins/
├── plugin1
│   ├── __init__.py
│   ├── LICENCE
│   ├── README
│   ├── static
│   │   └── plugin1
│   │       └── plugin1.css
│   └── templates
│       └── plugin1
│           └── plugin1.html
└── plugin2
    ├── __init__.py
    ├── LICENCE
    ├── README
    ├── static
    │   └── plugin2
    │       └── plugin2.css
    └── templates
        └── plugin2
            └── plugin2.html
disable_plugin(plugin_name)[源代码]

Disable a plugin (that is, create a DISABLED empty file) and restart the application to take effect

emit_dcp(event, **context)[源代码]

Emit a event(with dcp) and gets the dynamic join point data(html code).

参数:
  • event – str,unicode: A unique identifier name for dcp.
  • context – dict: Keyword parameter, additional data passed to the template
返回:

html code with Markup.

2.1.0 新版功能.

emit_func(cuin, *args, **kwargs)[源代码]

Emit a cuin(with dfp) and gets the func result.

参数:
  • cuin – str,unicode: Callback Unique Identifier Name.
  • args – list: Variable length parameter for cuin.
  • kwargs – dict: Keyword parameter for cuin.
返回:

The result of calling the function.

2.3.0 新版功能.

emit_tep(tep, typ='all', **context)[源代码]

Emit a event(with tep) and gets the template extension point data(html code).

Please use this function in the template. The extension point needs to be defined by itself.

Suppose you have an extension point named tep, only need to enable custom extension points in the template, for examples:

#: It can render HTML code and files for an template extension point,
#: or even pass in extra data at render time
{{ emit_tep('tep', data=data) }}
参数:
  • tep – str: Template extension point name, which is unique, a tep parsing result is list, within which can be HTML code and files
  • typ – str: Render type, default value = “all” all - render all, is default; fil - render HTML file; cod - render HTML code
  • context – dict: Keyword parameter, additional data passed to the template
返回:

html code with Markup.

emit_yep(yep)[源代码]

Gets the css extension point data(html code).

Please use this function between in the template, and the application needs to support multiple static folder functions, that is, the app initialized with Flask.

Assuming that the following templates need to be enabled for introducing CSS files using emit_metal, for examples:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    {{ emit_yep('yep') }}
</head>
<body>
    Your HTML Code
    {{ emit_tep('tep') }}
</body>
</html>
参数:yep – str: Name of css extension point, the only, a analytical results for the list or yep, can be used directly link CSS code
返回:html code with Markup.
enable_plugin(plugin_name)[源代码]

Enable a plugin (that is, create a ENABLED empty file) and restart the application to take effect

get_all_bep

Blueprint extension point

get_all_hep

Hook extension point.

  • before_request_hook, Before request (intercept requests are allowed)
  • before_request_top_hook, Before top request (Put it first)
  • after_request_hook, After request (no exception before return)
  • teardown_request_hook, After request (before return, with or without exception)
get_all_plugins

Get all plugins

get_all_tep

Template extension point

返回:dict: {tep: dict(HTMLFile=[], HTMLString=[]), tep…}
get_all_yep

Cascading style sheet (CSS) extension points.

返回:dict: {yep: [css…], …}
get_config

Your Flask-PluginKit Configure.

返回:dict

2.3.1 新版功能.

get_enabled_plugins

Get all enabled plugins

get_plugin_info(plugin_name)[源代码]

Get plugin information

init_app(app)[源代码]
logger = None

logging Logger instance

0.1.9 新版功能.

plugin_packages = None

Third-party plugin package from pypi, format:: [plugin1, plugin2, plugin…]

2.2.0 新版功能.

push_dcp(event, callback, position='right')[源代码]

Connect a dcp, push a function.

参数:
  • event – str,unicode: A unique identifier name for dcp.
  • callback – callable: Corresponding to the event to perform a function.
  • position – The position of the insertion function, right(default) and left.
Raises:

DCPError,NotCallableError: raises an exception

2.1.0 新版功能.

push_func(cuin, callback)[源代码]

Push a function for dfp.

参数:
  • cuin – str,unicode: Callback Unique Identifier Name.
  • callback – callable: Corresponding to the cuin to perform a function.
Raises:

DFPError,NotCallableError: raises an exception

2.3.0 新版功能.

s3 = None

Simple storage service(s3), currently optional: local or redis. May increase in the future: memcache. You can also inherit BaseStorage, custom storage interface.

1.3.0 新版功能.

storage(sf=None, args=None)[源代码]

Common storage interface with LocalStorage or RedisStorage, sf is a custom storage interface classes, args is its parameters, highest priority.

参数:
  • sf – class based BaseStorage
  • args – class init args
返回:

class instance

stpl = None

Template sorting

1.2.0 新版功能.

flask_pluginkit.flask_pluginkit.push_dcp(event, callback, position='right')[源代码]

Push a callable for PluginManager, push_dcp().

Example usage:

push_dcp('demo', lambda:'Hello dcp')

2.1.0 新版功能.

flask_pluginkit.flask_pluginkit.emit_config()[源代码]

Get Flask-PluginKit Configure from App Context, emit_config().

返回:dict

2.3.2 新版功能.

installer

Flask-PluginKit.installer

installer: install or remove plugin.

copyright:
  1. 2018 by staugur.
license:

BSD 3-Clause, see LICENSE for more details.

class flask_pluginkit.installer.PluginInstaller(plugin_abspath, **kwargs)[源代码]

基类:object

plugin installer for installing a compressed local/remote plugin

addPlugin(method='remote', **kwargs)[源代码]

Add a plugin, support only for .tar.gz or .zip compression packages.

参数:
  • methodremote, download and unpack a remote plugin package; local, unzip a local plugin package.
  • url – str: for method is remote, plugin can be downloaded from the address.
  • filepath – str: for method is local, plugin local absolute path
  • remove – Boolean: for method is local, remove the plugin source code package, default is False.
返回:

dict: add the result of the plugin, like dict(msg=str, code=int), code=0 is successful.

logger = None

logging Logger instance

1.0.1 新版功能.

removePlugin(package)[源代码]

Remove a plugin

参数:package – The plugin package name.

web

Flask-PluginKit.web

web: The server-side plugin management blueprint.

copyright:
  1. 2018 by staugur.
license:

BSD 3-Clause, see LICENSE for more details.

flask_pluginkit.web.blueprint = <flask.blueprints.Blueprint object>

Blueprint instance for managing plugins

0.1.6 新版功能.

fixflask

Flask-PluginKit.fixflask

fixflask: A class inheritance of flask, and added some additional functionality.

copyright:
  1. 2018 by staugur.
license:

BSD 3-Clause, see LICENSE for more details.

class flask_pluginkit.fixflask.Flask(import_name, static_url_path=None, static_folder='static', static_host=None, host_matching=False, subdomain_matching=False, template_folder='templates', instance_path=None, instance_relative_config=False, root_path=None)[源代码]

基类:flask_multistatic.MultiStaticFlask

before_request_second(f)[源代码]

Registers a function to run before each request. Priority Second.

..versionadded:: 2.4.0

before_request_top(f)[源代码]

Registers a function to run before each request. Priority First.

The usage is equivalent to the before_request() decorator, and before_request registers the function at the end of the before_request_funcs, while this decorator registers the function at the top of the before_request_funcs (index 0).

Because flask-pluginkit has registered all cep into the app at load time, if your web application uses before_request and plugins depend on one of them (like g), the plugin will not run properly, so your web application should use this decorator at this time.

1.0.1 新版功能.

utils

Flask-PluginKit.utils

utils: Some tool classes and functions.

copyright:
  1. 2018 by staugur.
license:

BSD 3-Clause, see LICENSE for more details.

class flask_pluginkit.utils.BaseStorage[源代码]

基类:object

get()[源代码]
index = 'flask_pluginkit_dat'
set()[源代码]
class flask_pluginkit.utils.LocalStorage[源代码]

基类:flask_pluginkit.utils.BaseStorage

get(key)[源代码]

Get persistent data from shelve.

返回:data
list

list all data

返回:dict
open(flag='c')[源代码]

Open handle

set protocol=2 to fix python3

1.3.1 新版功能.

set(key, value)[源代码]

Set persistent data with shelve.

参数:
  • key – string: Index key
  • value – All supported data types in python
Raises:
返回:

class flask_pluginkit.utils.RedisStorage(redis_url=None, redis_connection=None)[源代码]

基类:flask_pluginkit.utils.BaseStorage

get(key)[源代码]

get key data from redis

list

list redis key hash data

open(redis_url)[源代码]

open handler, you need install redis module

set(key, value)[源代码]

set key data

set_index(inedx)[源代码]

Set key name.

..versionadded:: 2.4.0

flask_pluginkit.utils.PY2 = False

check python version 2 or 3

1.3.1 新版功能.

flask_pluginkit.utils.isValidSemver(version)[源代码]

Semantic version number - determines whether the version is qualified. The format is MAJOR.Minor.PATCH, more with https://semver.org/

flask_pluginkit.utils.sortedSemver(versions, sort='asc')[源代码]

Semantically sort the list of version Numbers

exceptions

Flask-PluginKit.exceptions

Exception Classes

copyright:
  1. 2018 by staugur.
license:

BSD 3-Clause, see LICENSE for more details.

exception flask_pluginkit.exceptions.CSSLoadError[源代码]

基类:flask_pluginkit.exceptions.PluginError

exception flask_pluginkit.exceptions.DCPError[源代码]

基类:flask_pluginkit.exceptions.PluginError

exception flask_pluginkit.exceptions.DFPError[源代码]

基类:flask_pluginkit.exceptions.PluginError

exception flask_pluginkit.exceptions.InstallError[源代码]

基类:flask_pluginkit.exceptions.PluginError

exception flask_pluginkit.exceptions.NotCallableError[源代码]

基类:flask_pluginkit.exceptions.PluginError

exception flask_pluginkit.exceptions.PluginError[源代码]

基类:Exception

exception flask_pluginkit.exceptions.TarError[源代码]

基类:flask_pluginkit.exceptions.PluginError

exception flask_pluginkit.exceptions.VersionError[源代码]

基类:flask_pluginkit.exceptions.PluginError

exception flask_pluginkit.exceptions.ZipError[源代码]

基类:flask_pluginkit.exceptions.PluginError

贡献

Thank you for considering contributing to Flask-PluginKit!

问题反馈

报告问题

  • 描述你预期会发生什么。
  • 如果可能,请包含一个最小的、完整的、可验证的示例,以帮助我们识别问题。这还有助于检查问题是否与您自己的代码无关。
  • 描述实际发生了什么。如果有异常,包括完整的异常堆栈。
  • 列出您的Python、Flask、Flask-PluginKit版本。如果可能,检查新版本是否已经修复了这个问题。

提交pr

首先是环境

  • fork仓库 Flask-PluginKit
  • git克隆您账号下的Flask-PluginKit,并设置好您的 git config
  • 安装开发环境的依赖模块 pip install -r dev-requirements.txt

其次是编码

  • 编写代码,请尽量符合PEP8规范。
  • 编写测试用例和文档。
  • 分别使用pypy、py2.7、py3.4+环境运行测试 make dev && make test
  • 使用py3.4+环境可以生成文档 make dev && make html
  • 如果您贡献翻译文档,请再执行命令 make en ,然后翻译po文件,最后执行 make trans 生成翻译文档。

最后请求合并

  • 提交你的代码
  • 在GitHub上发起 pull request

TODO和DONE

TODO

  • Web管理页面插件安装和删除
  • 允许使用requirements.txt安装额外的包
  • 自定义web认证
  • web认证黑白名单
  • 模板代码、文件优先级
  • 模板中`emit_config`
  • Bug: stack, current_app

DONE

  • before_request_return扩展点
  • 注册静态css(register_css)
  • 注册静态css时分类
  • 模板扩展点include改造
  • 插件Web管理页面
  • web blueprint auth(only BOOL, after extension)
  • sphix rst docs
  • 允许重载uwsgi
  • 添加http basic auth等其他认证
  • 模板上下文排序
  • 插件配置和插件信息存储
  • 动态连接点,动态注册并执行函数将结果返回给模板使用
  • 插件版本号检测是否符合语义化版本2.0标准
  • 支持pypi第三方包
  • 支持推送扩展函数
  • before_request_second
  • redis cache with connection or redis_url
  • redis cache 自定义key

更新日志

v2.4.0

Released in 2019-04-12

  • feat: before_request_second
  • feat: redis cache with connection or redis_url
  • feat: redis cache 自定义key
  • feat: web访问认证选择BOOL类型时可以自定义认证成功字段
  • docs: update docs
  • style: update web manager html
  • chore: update LICENSE detail
  • chore: github org for flask-pluginkit(Official plugins)
  • chore: update test examples
  • chore: third plugin demo to https://github.com/flask-pluginkit/demo

v2.3.2

Released in 2019-04-09

  • fix: 注册before_request置顶
  • fix: 加载第三方插件异常详细提示
  • docs: 更新文档

V2.3.1

Released in 2019-04-09

  • fix: 动态函数扩展点以允许注册
  • feat: 增加自定义配置和筛选整合项目中相关配置

V2.3.0

Released in 2019-04-05

  • 支持推送扩展函数
  • 更新文档
  • 更新dcp回调函数检测

V2.2.0

Released in 2019-04-03

  • 支持本地插件和第三方插件

V2.1.1

Released in 2018-12-06

  • 修复插件版本号的问题

V2.1.0

Released in 2018-11-02

  • 修复bug,并将移除旧版本
  • 添加动态连接点(dcp),可以为某个标识点添加、删除函数并在模板种获取标识点函数执行的结果(HTML代码)
  • 更改 register_cepregister_hep, 请确保更改,谨慎升级
  • 更新文档

V2.0.0

Released in 2018-10-27

  • 支持Python3
  • 尽可能完整的文档
  • 存储索引由 flask_pluginkit.db 改为 flask_pluginkit_dat
  • 插件元数据中插件名称由 __name__ 改为 __plugin_name__, 以避免与python冲突, 请谨慎升级到此版本

V1.3.0

Released in 2018-10-15

  • 简单存储服务,用来给插件提供数据存储和读取
  • 本地存储和redis存储
  • 自定义存储

V1.2.0

Released in 2018-10-11

  • 模板排序
  • 支持HTTP Basic Auth认证方式

V1.0.2

Released in 2018-10-09

  • 重载uwsgi

V1.0.1

Released in 2018-10-08

  • 修复插件依赖于before_request的问题
  • 集成支持多静态文件夹

V1.0.0

Released in 2018-09-27

  • 使用sphinx制作接口文档

V0.1.10

Released in 2018-09-25

  • 调整register_yep,改为更灵活的方式分类载入css文件
  • 支持emit_tep渲染模板时传递额外数据
  • 优化部分代码

V0.1.9

Released in 2018-09-24

  • 修复bug
  • 插件Web管理页面:支持认证

V0.1.8

Released in 2018-09-22

  • 层叠样式表扩展点,可在模板中引用
  • 模板扩展点使用更改,去除get_tep、get_tep_string,使用emit_tep代替,支持包含模板和HTML代码

V0.1.7

Released in 2018-09-20

  • 修复bug
  • 不支持python2.6

V0.1.6

Released in 2018-09-19

  • 插件Web管理页面:启用、禁用插件,重启应用

V0.1.4

Released in 2018-09-09

  • Add before_request_return CEP

V0.1.3

  • Flask扩展,以支持应用插件式开发
  • 支持上下文扩展点、模板扩展点、蓝图扩展点
  • 模板扩展点支持HTML代码和文件
  • 插件支持添加静态文件(需要安装flask-multistatic扩展)
  • 插件安装管理(从url或local安装插件zip、gz包)