百木园-与人分享,
就是让自己快乐。

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

来源:https://www.cnblogs.com/lghgo/p/15978176.html
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » Flask 自建扩展

相关推荐

  • 暂无文章