在flask中如何自定义自己的扩展包并上传到官网

在flask中构建自己的模块

创建工程
让我们先创建一个工程,目录结构如下:

1
2
3
4
5
6
flask-logging/
├ LICENSE # 授权说明
├ README # 项目介绍
├ setup.py # 打包分发文件
└ flask_logging/ # 扩展代码包
└ __init__.py # 扩展代码

根据Flask扩展命名规范,扩展名必须为”Flask-Logging”形式,以”Flask-“为前缀,后面的单词首字母大写。扩展的代码必须放在名为”flask_logging”的包下,注意这里是下划线,与扩展名中的横线不同,单词都小写。”LICENSE”和”README”文件都是审核必须的,关于审核部分,我们会在后面介绍。

编写分发文件
接下来,我们写”setup.py”文件,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Flask-Logging
-------------
Log every request to specific view
"""
from setuptools import setup

setup(
name='Flask-Logging',
version='1.0',
url='http://example.com/flask-logging/',
license='BSD',
author='Billy J. Hee',
author_email='billy@bjhee.com',
description='Log every request to specific view',
long_description=__doc__,
packages=['flask_logging'],
zip_safe=False,
include_package_data=True,
platforms='any',
install_requires=[
'Flask'
],
classifiers=[
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules'
]
)

这里需要注意几点:

扩展名的格式必须为”Flask-Logging”,上节介绍过
必须指定url链接到扩展主页或文档
“zip_safe”必须为False
“install_requires”必须列出所有依赖的库
编写扩展代码
进入主题了,由于我们的扩展相当简单,因此所有代码都放在了init.py中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#coding:utf8
from flask import current_app, request
from functools import wraps
from logging.handlers import TimedRotatingFileHandler
import logging
import time

# 指定日志文件名,日志级别,及日志记录格式
entry_log = TimedRotatingFileHandler('entry.log','D')
entry_log.setLevel(logging.DEBUG)
entry_log.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))

class Logging:
# 构造函数
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app)

# 初始化应用
def init_app(self, app):
app.logger.addHandler(entry_log)

# 视图装饰器,被装饰的视图将自动记录访问日志
def log_entry(self, func):
app = self.app or current_app
@wraps(func)
def decorator(*args, **kwargs):
start = time.time()
# 记录请求开始
app.logger.debug('Start request call: %s' % request.url)
ret = func(*args, **kwargs)
# 记录请求结束
app.logger.debug('Finish request call: %s' % request.url)
duration = time.time() - start
# 记录请求所耗时长
app.logger.debug('Request: %s consumed %f s' % (request.url, duration))
return ret

return decorator

代码逻辑都在写注释里了,这个扩展提供了log_entry视图装饰器,来记录视图访问日志。这里同样要注意几个重要的部分:

构造函数init()和初始化函数init_app()是必须的
如果构造函数传入了app,则调用init_app(),这样确保两者功能一致
构造函数里我们设置了self.app=app,而init_app()没有,这是为什么呢?这是一种规范,或者说习惯。当系统只有一个app时,建议使用构造函数初始化扩展对象,这时对象中的app就指向这一个应用。而当系统有多个应用同时存在,比如说应用工厂或测试场景下,建议使用init_app()来初始化扩展对象,这样扩展对象不会指向任何应用
在视图装饰器里,我们使用了app = self.app or current_app来获取当前应用,这分别对应于上一点说的单个应用及多个应用场景
因为视图装饰器是视图访问时被调用,所以此时应用上下文和请求上下文都存在,因此我们可以访问到current_app和request对象。离开上下文的话,就无效了
扩展写完了,让我们来测试一下,创建一个Flask应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
from flask_logging import Logging

app = Flask(__name__)
logging = Logging(app)

@app.route('/')
@logging.log_entry
def index():
return '<h1>Hello World</h1>'

if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)

启动应用,访问http://localhost:5000/。查看下代码当前路径,是不是出现了”entry.log”文件,并且记录了URL请求日志?

关于审核
个人没有发起过审核申请,对于具体流程尚不清楚。不过如果你要将自己的扩展提交官方审核,至少要做到下面几点:

扩展代码在包flask_myext下,审核通过后,Flask会设置一个重定向包flask.ext.myext来指向你的包。对于用户来说,官方扩展建议导入flask.ext.myext格式的包
必须提供一个”setup.py”分发文件,并在PyPI上注册,这样用户就可以通过pip install来安装你的扩展
必须提供”LICENSE”文件,并且授权是BSD, MIT或WTFPL
必须提供”README”文件及文档,文档是由Sphinx生成
必须同时提交单元测试代码
必须支持Python 2.6和2.7版本