Coder Social home page Coder Social logo

chinapnr / py-summer Goto Github PK

View Code? Open in Web Editor NEW
3.0 4.0 1.0 74 KB

Simplify and strengthen the Python web server development, mainly RESTful server, use Flask as the backend.

License: GNU General Public License v3.0

Python 54.28% Smarty 44.68% Shell 1.03%

py-summer's Introduction

Build Status Coverage Status Documentation Status

py-summer

一个快速生成 Python Web 项目框架的工具,用户无需考虑后端框架(即支持多后端,目前支持 Flask )。支持自定义生成项目层级结构、接口。内容包含演示代码、测试用例等。

安装

Install and update using pip:

pip install -U py-summer

示例

  1. 生成一个新项目

    summer create -n test_project -d ./
    
  2. 生成的项目层级结构

    │  .gitignore                            ignore 文件
    │  gunicorn.conf                         gunicorn 配置文件
    │  requirements.txt                      项目运行所需依赖包
    │  server.py                             项目入口文件
    ├─application                            application
    │  │  __init__.py
    │  │  router.py                          路由
    │  ├─controller
    │  │      hello_controller.py            controller
    │  ├─model
    │  │      hello_model.py                 model
    │  └─view
    │         hello_handler.py               handler
    ├─config
    │      config.py                         配置文件
    ├─docker                                 docker 文件夹
    │      docker-compose.yml
    │      start.sh
    ├─log                                    日志文件夹
    ├─test                                   测试用例
    │      conftest.py
    │      test_api.py
    │      test_db.py
    └─tools                                  辅助方法
            error.py
    
  3. 运行项目

    python server.py
    

    默认监听所有地址, 端口为 8080。测试页可访问 http://127.0.0.1:8080/api/test

生成的项目模块说明

通过上面的示例,我们生成了一个简单的 http web 服务,下面将对生成的目录中各模块做一下介绍,简单的文件已经在目录结构中有说明,这里将不在赘述。

  1. application

这里是 web 应用程序的总目录,里面包含 model/view/controller, 即 web 服务的 MVC 部分,router.py 这里是设置整个项目路由的地方。

  1. config

config 目录是整个工程的配置目录,里面通过类的形式来进行各种环境的配置,通过 application 下的 __init__.py 来导入配置信息。

  1. docker

docker 中是 docker-compose 的配置文件,用来配置项目的部署环境,start.sh 是 docker 容器启动的入口文件。

  1. test

test 这里是项目的单元测试模块,里面有一个 conftest.py 文件来配置测试信息,test_xxx.py 文件是具体的单元测试文件,这里使用 pytest.fixture 打通 server/client 进行测试,测试时无需额外启动 server 即可测试 server 接口。

后续计划

  1. 集成 Flask 常用功能模块

    目前计划集成功能点如下:
    • 启动方式
    • 路由处理
    • 数据库处理
    • 异常处理
    • 常用插件
  2. 支持接口根据配置文件自动生成,进一步提高代码规范,开发效率。

py-summer's People

Contributors

itaa avatar wingfish avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar James Cloos avatar  avatar  avatar

Forkers

ruivers

py-summer's Issues

生成文档,Travis-ci失败排查

生成文档

使用 Read the Docs 生成文档

Travis-ci失败排查

在之前的过程中,travis-ci出现了以下问题:

   def connect(self, *cargs, **cparams):
>       return self.dbapi.connect(*cargs, **cparams)
E       sqlite3.OperationalError: unable to open database file

问题是出现在test_db中,在conftest中会将创建的dev.sqlite删除,代码如下:

@pytest.yield_fixture()
def db():
    Base.metadata.create_all(engine)
    yield db
    Base.metadata.drop_all(engine)

初步判断是因为删除了文件,所以本次将不删除dev.sqlite再次进行travis-ci测试

跟进

a. 得到一个新的错误:

ModuleNotFoundError: No module named 'py_summer'
ERROR: could not load /home/travis/build/chinapnr/py-summer/test/conftest.py

解决方案:
修改travis.yml

# build package test
before_install:
python setup.py sdist
pip install ./dist/py_summer-1.0.0.tar.gz

b. 上述方案执行后travis仍然失败,得到以下错误:

E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) unable to open database file (Background on this error at: http://sqlalche.me/e/e3q8)

仔细分析,错误中dev.sqlite的目录为**/home/travis/build/chinapnr/py-summer/db/dev.sqlite**, 而我们本地生成的目录为test下的db,查看具体函数:

def get_db_sqlite_uri(db_name):
    """
    获取sqlite地址

    :param:
        * db_name(str): 数据库名称
    :return:
        * db_sqlite_uri(str): sqlite链接地址
    """
    db_sqlite_uri = 'sqlite:///' + fff.get_abs_filename_with_sub_path('db', db_name)[1]
    return db_sqlite_uri

本地执行db_sqlite_uri返回的路径为test下的db文件夹中的dev.sqlite,而travis得到的目录为项目根目录下的db文件夹,所以最后在项目根目录中新建db文件夹,问题得到解决。

后续

针对本次出现的问题(get_db_sqlite_uri返回的路径win下和linux下不一致题),会新建一个issus来跟踪。也反映出自己排除问题不细致的问题,早点发现路径不对的话会早点解决问题。

unittest, 抽取 conftest.py, 集成 unittest 基础配置

unittest, 抽取 conftest.py, 集成 unittest 基础配置
目前自动生成的 conftest.py 内容如下,该部分需提取到 summer 内,用户使用的时候直接 import 即可

import os
import pytest
import tempfile
from application.router import app as testapp
from application import db, engine, Base


@pytest.fixture
def app():
    db_fd, db_path = tempfile.mkstemp()
    yield testapp, db_path
    os.close(db_fd)
    os.unlink(db_path)


@pytest.yield_fixture()
def db():
    Base.metadata.create_all(engine)
    yield db
    Base.metadata.drop_all(engine)


@pytest.yield_fixture
def client(app):
    with app[0].test_client() as cli:
        yield cli

将应用打包,直接导入使用

简单使用示例:

目录结构如下:

  • project_name
    • app.py
    • config.py

app.py

from py_summer import create_server, jsonify
summer = create_server('config')  # 传入自己项目的配置文件,不传默认使用py_summer中的

@summer.route('/hello')
def say_hello():
    return jsonify(summer.config)

if __name__ == '__main__':
    print('Summer server started on port %s.' % summer.config['IP_PORT'])
    summer.run(host='127.0.0.1', port=summer.config['IP_PORT'])

config.py

class Config(object):
    DEBUG = False
    TESTING = False

class DevelopmentConfig(Config):
    """
    开发环境配置
    """
    DEBUG = True
    IP_PORT = 9090
    DEFAULT_TOKEN = 'flzx3qc'

Extension 设计具体实现

以 FlaskExtension 举例:
设计一个 BaseExtension,其中抽象出常用方法,例如 load_conf, create_server 等。这里可利用 Python 中的元类 metaclass,以及 abc 中的 abstractmethod 实现

class BaseExtension(metaclass=ABCMeta):
    # BaseExtension 实现各抽象方法
    @abstractmethod
    def load_conf(self, application, *args, **kwargs):
        pass

    @abstractmethod
    def create_server(self):
        pass

FlaskExtension 继承 BaseExtension,并实现相关方法

class FlaskExtension(BaseExtension):
    def __init__(self):
        pass

    def load_conf(self, application, *args, **kwargs):
        pass

    def create_server(self):
        pass

这么设计的目的在于提高拓展性,降低耦合,例如要实现一个 SanicExtension,也只需要实现相对应的 load_conf, create_server 即可

支持 flask 的命令行方式以及测试命令行创建数据表

flask.cli 中直接支持 click,但是这样会有一个兼容性问题,如果我们用 sanic 这类不直接支持 click 的 server 包,就会不行。

考虑还是使用目前 jman 的做法,用 click,但是 click 写法本身和 flask 无关,只和 server 逻辑有关,这样以后支持更多 server 的话,命令行方式可以继续兼容。

通过 summer.py create_tables 来创建默认的数据库

Plugin 设计具体实现

Plugin 中,目前处理用户框架的选择和实现对应的 run 方法。

如何选择框架:

summer = Summer()
summer.setFramework = "Flask"

也可以不选择,不选择默认为 Flask,具体实现如下:

class Plugin(object):

    def __init__(self):
        # 当前支持的框架
        self.supportFramework = ["Flask"]

        # 默认使用 Flask
        self.frameworkName = "Flask"

    @property
    def setFramework(self):
        return self.frameworkName

    @setFramework.setter
    def setFramework(self, framework_name):
        if framework_name in self.supportFramework:
            self.frameworkName = framework_name
        else:
            raise ImportError("This service framework is not supported for the time being")

run 方法运行 extension:

def run(self):
        # 动态载入相关模块
        extensionModuleName = self.frameworkName + Plugin.extensionSuffix
        extensionModule = importlib.import_module("extension")
        if hasattr(extensionModule, extensionModuleName):
            extensionObject = getattr(extensionModule, extensionModuleName)()
            application = extensionObject.create_server()
        else:
            raise ImportError("not found" + extensionModuleName)
        return application

文档,使用说明文档补充

文档,使用说明文档补充

需涵盖所有支持功能的介绍,详细介绍使用生成后的工程的使用方法, db/config/test 等需介绍

flask app 增加sqlite 路径和文件名支持

对于重构的 summer 中的 flask server 对象增加本地数据库支持,比如 sqlite。

沿用 jman 中的方式,在 config.py 中,sqlite 路径通过下面这样形式:

# 生成 sqlite 数据库的 uri 字符串
def get_db_sqlite_uri(db_name):

    db_sqlite_uri = 'sqlite:///' + fff.get_abs_filename_with_sub_path('db', db_name)[1]

    return db_sqlite_uri

可以在当前 work 路径下访问 db 路径下的 sqlite 数据库文件。

然后通过 config 对象返回:

class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = get_db_sqlite_uri('jdb_dev.sqlite')

根据环境变量去获取配置

根据环境去加载配置,实例化对象的时候直接传入环境变量名称,默认配置路径为summer.config,也可传入传入config路径(待优化,可用于打包后上层应用传入自己的配置文件)
例子:config['DEBUG'] 取用当前环境下的DEBUG的值,没有则返回None

class ConfigManager(object):
    config_map = {
        'development': 'DevelopmentConfig',
        'testing': 'TestingConfig'
    }

    def __init__(self, env_key, config_dir=None):
        config_dir = config_dir if config_dir else '.'.join(['summer', 'config'])
        self._content = dict()
        self.current_config_obj = None
        self.env_key = env_key
        # 默认加载development配置
  

    def __getitem__(self, item):
        # 获取环境变量中配置的值

    def read(self, relative_path):
        # 找到对应的配置文件,并导入
        return self

flask 中的 db 对象初始化处理

1 如果 flask 不需要数据库,允许不生成 db 对象
2 db 对象如何返回给具体 server 这里,数据库表的 models 逻辑属于 server,不属于框架本身

在 init.py 中,通过:

# 创建 app server 对象
app = create_server()
# 创建 sqlalchemy 对象
db = SQLAlchemy(app)

创建 app 和 db 对象,然后在 models.py 中,就可以 import db 来进行相关设置。

3 如果 sqlite 文件不存在,允许自动创建sqlite 数据库

拓展 py-summer

本次设计如下:

image

Summer, 提供统一对外的入口。

Plugin, Summer 的父类,在 Plugin 中,主要的功能为处理用户选择不同的 Framework,并从后面 extension 中动态载入相关的 Framework。

extension,功能模块或者 Framework 的拓展。以 FlaskExtension 举例,其中有 create_server 方法创建 application 对象,load_conf 载入配置等。

这样设计降低了耦合性,方便拓展。

修改 test 或者 dev 环境的设置方式

在jman 中实现是通过配置文件中设定 testing,development 等的,根据最佳实践的原则,修改为环境变量方式。

也就是不再用下面这类方式
···
server_status = jarvis_man.dt['server']['status']
···

而是用
export FLASK_ENV=development
这样来定义

并在配置文件中类似下面方式读入。

···
ENVIRONMENT_DEBUG = os.environ.get("DEBUG", default=False)
if ENVIRONMENT_DEBUG.lower() in ("f", "false"):
ENVIRONMENT_DEBUG = False
···

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.