• Flask 自建扩展


    • 自建扩展介绍

      • Flask扩展分两类
        1. 纯功能, 如: Flask-Login 提供用户认证
        2. 对已有的库和工具包装(简化继承操作,并提供有用的功能,更方便)
          如: Flask-SQLAlchemy 包装了 SQLAlchemy
      • 涉及的 python 包
        1. setuptools
        2. wheel
        3. twine: 发布python 包 (发布到 PyPI 后才能使用 pippipenv 安装)
        4. readme_renderer: 将 mdrsttxt 文本 渲染成.html
      • 命名:
        1. 扩展的名称: Flask-<功能/第三方库名> 或 <功能/第三方库名>-Flask
        2. 扩展的包名: 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('''<link rel="stylesheet" href="%s">\n
              	<script src="%s"></script>'''% (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('''
              	<div class="social-share %s" data-sites="%s" data-mobile-site="%s"align="%s">
                  %s</div>'''%(addition_class, sites, mobile_sites,align, title ))
              
          
        • 在模板中使用
          {{ share.create('分享到:') }}
          
    • 开源发布准备

        1. 添加文档字符串与注释后的完整代码
        """
            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, request
        
        class 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.
            """
           
        
        1. 编写 README 与文档
        • 小项目 直接用 README概括所有的必需的说明
        • 大项目 比较复杂的,多文件组织文档内容
          将项目部署到 Read the Docs
          Sphinx + Github + Readthedocs的工作流编写和部署文档
        1. 定义 python 包的元数据:(setup.py)
        """
        	Flask-Share
            
            Create 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 setup
        
        basedir = 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=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']
          ],
        )
        
        1. 指定打包其他文件: MANIFEST.in
          需要在 setup()方法中设置: include_package_data=True
        graft flask_share/static
        include LICENSE test_flask_share.py
        # exclude 用来排除匹配文件
        # recursive-include 递归匹配
        # recursive-exclude 递归排除匹配
        # graft 目录 包含目录下所有
        # prune 目录 配出目录下所有
        
        1. 编写单元测试
        import unittest
        
        from flask import Flask, render_template_string, current_app
        from flask_share import Share
        
        class 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 = response.get_data(as_text=True)
            self.assertIn('social-share.min.js', data)
            self.assertNotIn('<div class="socail-share"', data))
         
        
        1. 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 上传
        • 安装 twine
          pipenv install twine --dev
          
        • 上传
          twine upload dist/*
          
    • 编写良好的扩展

      • 命名规范(Flask-Foo 或 Foo-Flask)
      • 使用相对宽松的开源许可证(MIT/BSD)
      • 支持工厂模式(添加 initi_app() 方法)
      • 支持同时运行多程序实例( 使用 current_app 获取程序实例)
      • 包含 setup.py脚本,并列出所有安装依赖(必需)
      • 包含单元测试
      • 编写文档并在线发布
      • 上传到 PyPI
  • 相关阅读:
    pytest-xdist分布式测试原理浅析
    数据结构学习笔记 - 带权并查集(食物链题解)
    zk的watch机制使用及原理分析
    网络安全(黑客)自学
    训练记录day13 (想起来今天还没发博客
    2022年最新运维常用脚本学习
    使用kubasz快速搭建Kubernetes集群
    前端应用发布到nodejs server后浏览器刷新404问题
    Linux学习笔记 --- 初识Linux
    pip安装apex报错ERROR: Could not build wheels for cryptacular.......
  • 原文地址:https://www.cnblogs.com/lghgo/p/15978176.html