首页 > it技术 > >
自建扩展介绍
Flask扩展分两类
纯功能, 如: Flask-Login 提供用户认证
对已有的库和工具包装(简化继承操作,并提供有用的功能,更方便) 如: Flask-SQLAlchemy 包装了 SQLAlchemy
涉及的 python 包
setuptools
:
wheel
:
twine
: 发布python
包 (发布到 PyPI
后才能使用 pip
或 pipenv
安装)
readme_renderer
: 将 md
、rst
、txt
文本 渲染成.html
命名:
扩展的名称: Flask-<功能/第三方库名> 或 <功能/第三方库名>-Flask
扩展的包名: flask_<功能/第三方库名> (小写加下划线)
扩展类实现
编写扩展类(以 Flask-Share 为例)
使用扩展步骤: 导入扩展类 - 实例化 - 传入 app 初始化from flask_share import Share
share = Share()# extensions.py 中统一实例化所有扩展
share.init_app(app) # 在工厂函数中统一初始化所有扩展
# 也可以一步到位
# share = share(app)
新建扩展类 (flask_share/__init__.py
)class Share(object):
def __inti__(self, app=None):
self.init_app(app)def init_app(self, app):
# 兼容 0.7 以前版本
if not hasattr(app, 'extensions'):
app.extensions={}# 在 app 应用中存储所有扩展实例, 可验证扩展是否完成实例化
app.extensions['share'] = self# 扩展类添加到模板上下文中
app.jinja_env.globals['share'] = self
# app.context_processor(lambda:{'share': self})# 扩展配置, 初始化后添加到 app.config 中, 以 SHARE_ 开头避免冲突
app.config.setdefault('SHARE_SITES', 'weibo,wechat,douban,facebook,twitter,google,linkedin,qq,qzone')
app.config.setdefault('SHARE_MOBILESITES','weibo,douban,qq,qzone')
app.config.setdefault('SHARE_HIDE_ON_MOBILE', False)
app.config.setdefault('SHARE_SERVER_LOCAL', False) # 是否使用内置资源
实现扩展功能
加载静态资源class Share(object):
@staticmethod
def load(css_url=None, js_url=None):if current_app.config('SHARE_SERVE_LOCAL'):# 使用本地进入条件
css_url = url_for('share.static', filename='css/share.min.css')
js_url = url_for('share.static', filename='js/share.min.js')if css_url is None:
css_url = 'https://cdn.bootcss.com/social.share.js/1.0.16/css/share.min.css'
if js_url is None:
js_url = 'https://cdn.bootcss.com/social-share.js/1.0.16/js/social-share.min.js'
return Markup(''' \n
'''% (css_url, js_url))def init_app(self, app):
# app.static_url_path 的引用是为了和用户设置一致
blueprint = Blueprint('share', __name__, static_folder='static',
static_url_path='/share'+ app.static_url_path)
app.register_blueprint(blueprint)
创建前端分享组件class Share(object):
@staticmethod
def create( title='', sites=None, mobile_sites=None,align='left',addtion_class=''):
if sites is None:
sites = current_app.config['SHARE_SITES']
if mobile_sites is None:
mobile_sites = current_app.config['SHARE_MOBILE_SITES']return Markup('''%s'''%(addition_class, sites, mobile_sites,align, title ))
在模板中使用{{ share.create('分享到:') }}
开源发布准备
添加文档字符串与注释后的完整代码
"""
Flask-Share
# ~~~~~~~~~~~~~~
Create social share component in Jinja2 tempalte based on share.js.
:copyright: (c) 2017 by Gavin Li.
:license: MIT, see LICENSE for more details.
"""import re
from flask import current_app, url_for, Markup, Blueprint, requestclass Share(object):@staticmethod
def load(css_url=None, js_url=None):
""" Load share.js resourse.:param css_url: if set, will be used as css url
:param js_url: if set, will be used as js url
:param serve_local: if set to True, the local resource will be used
"""@staticmethod
def create( title='', sites=None, mobile_sites=None,align='left',addtion_class=''):
""" Create a share component.:param title: the prompt displayed on the left of the share component.
:param sites: a string that consist of sites, separate by comma.
:param mobile_sites: a string that consist of sites, separate by comma.
supported site name: weibo, wechat, douban, facebook, twitter, google, linkedin, qq, qzone."
for example: weibo,wechat, qq.
:param mobile_sites: the sites displayed on mobile.
:param align: the align of the share component,default to '`left`'.
:param addition_class: the style class added to the share component.
"""
编写 README 与文档
小项目 直接用 README概括所有的必需的说明
大项目 比较复杂的,多文件组织文档内容 将项目部署到 Read the Docs上 Sphinx + Github + Readthedocs的工作流编写和部署文档
定义 python 包的元数据:(setup.py
)
"""
Flask-ShareCreate social share component in Jinja2 template based on share.js.
:copyright: (c) 2022 by Gavin li.
:license: MIT, see LICENSE for more details.
"""
form os import path
from codecs import open
form setuptools import setupbasedir = path.abspath(path.dirname(__file__))# Get the long description from the README file
with open(path.join(basedir,'README.md'), encoding='utf-8') as f:
long_description = f.read()setup(
name='Flask-Share', # 包名称
version='0.1.0',# 版本
url='https://github.com/lghpython/flask-share',
license='MIT',
author='xxx'
author_email='xx@xx.com',
description='xxx',
long_description=long_description,
long_description_content_type='text/markdown', # 默认渲染格式为 rst
platforms='any',
packages=['flask_share'], # 包含的包列表,包括子包,可用find_pakages()
zip_safe=False,
test_suite='test_flask_share', 测试包或模块
include_package_data=https://www.it610.com/article/True,
install_requires=['Flask'],# 安装依赖
keywords='flask extension development', # 项目关键词
classifiers=[ # 分类词, 在 PyPI 中设置分类
'DevelopmentStatus::3-Alpha',
'Environment::WebEnvironment',
'IntendedAudience::Developers',
'License::OSIApproved::MITLicense',
'ProgrammingLanguage::Python',
'ProgrammingLanguage::Python::2',
'ProgrammingLanguage::Python::2.7',
'ProgrammingLanguage::Python::3',
'ProgrammingLanguage::Python::3.3',
'ProgrammingLanguage::Python::3.4',
'ProgrammingLanguage::Python::3.5',
'ProgrammingLanguage::Python::3.6',
'Topic::Internet::WWW/HTTP::DynamicContent',
'Topic::SoftwareDevelopment::Libraries::PythonModules']
],
)
指定打包其他文件: MANIFEST.in 需要在 setup()方法中设置: include_package_data=https://www.it610.com/article/True
graft flask_share/static
include LICENSE test_flask_share.py
# exclude 用来排除匹配文件
# recursive-include 递归匹配
# recursive-exclude 递归排除匹配
# graft 目录 包含目录下所有
# prune 目录 配出目录下所有
编写单元测试
import unittestfrom flask import Flask, render_template_string, current_app
from flask_share import Shareclass ShareTestCase(unittest.TestCase):def setUp(self):
self.mobile_agent={{'HTTP_USER_AGENT':'Mozilla/5.0(iPhone;
CPUiPhoneOS9_1likeMacOSX)\
AppleWebKit/601.1.46(KHTML,likeGecko)Version/9.0Mobile/13B143Safari/601.1'}}
app = Flask(__name__)
app.testing=True
self.share=Share(app)@app.route('/')
def index():
return render_template_string('{{share.load() }}\n {{share.create() }}')
# 推送上下文
self.context=app.app_context()
self.context.push()
self.client - app.test_client()def tearDown(self):
self.context.pop()def test_create_on_mobile(self):
current_app.config['SHARE_HIDE_ON_MOBILE'] = True
response = self.client.get('/', environ_base=self.mobile_agent)
data = https://www.it610.com/article/response.get_data(as_text=True)
self.assertIn('social-share.min.js', data)
self.assertNotIn('
setup.cfg
发布到 PyPI
创建 PyPI 账号
注册访问
方便访问: 创建 .pypirc文件, 放置$HOME/.pypirc(win) 或~/.pypir(mac linux) 明文密码限制访问权限[distutils]
index-servers=
pypi[pypi]
username: 用户名
password: 密码
setuptools
打包
创建 Source Distributions 包python setup.py sdist
创建 Wheel 包python setup.py bdist_wheel
合并命令python setup.py sdist bdist_wheel
twine 上传
安装 twinepipenv install twine --dev
上传twine upload dist/*
编写良好的扩展
命名规范(Flask-Foo 或 Foo-Flask)
使用相对宽松的开源许可证(MIT/BSD)
支持工厂模式(添加 initi_app() 方法)
支持同时运行多程序实例( 使用 current_app 获取程序实例)
包含 setup.py脚本,并列出所有安装依赖(必需)
包含单元测试
编写文档并在线发布
上传到 PyPI
推荐阅读