项目框架管理工具pyouter的实践


无论是个人开发还是团队协作,在进行代码开发的时候,统一的代码规范是十分重要的。
这里说的代码规范指的不是变量或者函数的命名规范,而是项目结构的规范。

在这里,我要介绍一个由@幻灰龙@ccat开发的开源项目pyouter。这个项目很好的为开发者提供了一个规范的项目框架,不管是个人开发还是团队开发,都可以通过使用这套框架来共同规范起开发逻辑。

pyouter的内部原理实现可以参考https://blog.youkuaiyun.com/huanhuilong/article/details/121481377或者https://github.com/fanfeilong/pyouter,这篇文章只做项目的实践介绍。

测试项目的github地址为pyouter_test

pip包的安装

# https://pypi.org/project/pyouter/0.0.1
pip install pyouter

项目目录结构

假设我们开发的项目名为pyouter_test,我们的项目目录应该如下所示:

  • pyouter_test
    • src
      • data(可自定义)
      • test(可自定义)
      • server(可自定义)
      • config
        • config.py
      • main.py
      • options.py

项目实现

定义好我们的目录结构之后,首先进行main.py和options.py的实现。
main.py是我们整个项目的入口,options.py是实现整个项目结构的逻辑

main.py

from pyouter.app import App
from pyouter.router import Router

from options import get_args_parser
from config.config import load_config

def main_dispatch():
    """ 主路由

    """


    router = Router(
        init = lambda config,options: print('server_ip:{}'.format(config['server_ip'])),
    )

    return router


def run():
    """ 入口函数
    
    """

    # 获取命令行参数
    args_parser = get_args_parser()
    options = args_parser.parse_args()
    # 加载配置文件
    config = load_config(options)

    app = App(
        config=config,
        parser=args_parser
    )

    app.use(
        router=Router(
            pyouter_test = main_dispatch()
        )
    )

    app.run()


if __name__=="__main__":
    run()

options.py

from argparse import ArgumentParser
from pyouter.default import create_parser


def add_custom_arguments(parser: ArgumentParser):
    ''' 用户定制化参数选项
    
    * --cluster: 开发环境还是线上环境
    '''
    parser.add_argument(
        "--cluster",
        dest="cluster",
        help="cluster dev or pro",
        default="dev",
        nargs='?',
        type=str,
        metavar="CLUSTER"
    )



def get_args_parser():
    parser = create_parser("pyouter test")
    add_custom_arguments(parser)

    return parser


def show_help():
    """
    命令行选项说明:
    ==
    """

    help = '\n'.join([
        show_help.__doc__,
        add_custom_arguments.__doc__
    ])

    print(help)

我们在main函数里面配置好了整个项目的入口,在options.py里面设置了一个参数"–cluster"。
我们还在main函数里面调用了config/config.py里面的load_config函数,这个函数load了一个字典格式的config,方便我们对项目的环境进行配置,类似以下代码:

config.py

def load_config(options):
    if options.cluster == 'dev':
        config = {'server_ip':'127.0.0.1'}
    elif options.cluster == 'pro':
        config = {'server_ip':'192.168.0.1'}
    return config

写完上面三个python代码后可以在src目录下运行命令

python main.py pyouter_test.init --cluster dev

会打印结果
在这里插入图片描述
如果运行命令

python main.py pyouter_test.init --cluster pro

则会打印结果
在这里插入图片描述

二级叶子结点test实现

综上,项目的基本框架已经实现了,接下来实现叶子结点的函数。
例如,我们在src/test/test_hello.py有一个函数

def test_helloworld(config,options):
	print('hello world!')

我们要怎么直接运行这个函数呢?
首先,我们需要在src/test/下面定义一个__init__.py

from pyouter.router import Router

def dispatch():
    from test.test_hello import test_helloworld

    router=Router(
        test_hello = test_helloworld
    )
    return router

然后在main.py里面修改main_dispatch()函数

def main_dispatch():
    """ 主路由

    """
    from test import dispatch as test_dispatch

    router = Router(
        init = lambda config,options: print('server_ip:{}'.format(config['server_ip'])),
        test = test_dispatch()
    )

    return router

接下来运行命令

python main.py pyouter_test.test.test_hello --cluster dev

则会直接运行test_helloworld函数,终端打印
在这里插入图片描述

三级叶子结点server实现

上面的test_helloworld函数只是二级的叶子结点实现,假如我们要进行一个三级的叶子结点实现可以参考以下的server实现。
假如我们在src/data/目录下有一个data.txt文件,里面只有三行数据

test
server
data

我们在src/server/data/read_data.py里面有个函数read_data_txt函数

def read_data_txt(config,options):
    data_path = './data/data.txt'
    with open(data_path) as f:
        for line in f:
            print(line + ' ')

如果我们要直接运行这个函数,我们还需要分别在src/server/src/server/read_data/下面分别定义一个__init__.py
src/server/data/下面的__init__.py实现如下:

from pyouter.router import Router

def dispatch():
    from server.data.read_data import read_data_txt

    router=Router(
        read_data_txt = read_data_txt
    )
    return router

src/server/下面的__init__.py实现如下:

from pyouter.router import Router

def dispatch():
    from server.data import dispatch as data_dispatch

    router=Router(
        data = data_dispatch()
    )
    return router

然后修改main.py里面的main_dispatch()函数:

def main_dispatch():
    """ 主路由

    """
    from test import dispatch as test_dispatch
    from server import dispatch as server_dispatch

    router = Router(
        init = lambda config,options: print('server_ip:{}'.format(config['server_ip'])),
        test = test_dispatch(),
        server = server_dispatch()
    )

    return router

接下来运行命令

python main.py pyouter_test.server.data.read_data_txt --cluster dev

终端会打印结果
在这里插入图片描述

项目结构细节

上述实现的整体项目类似于一颗树,根目录是src/main.pytest_helloworldread_data_txt函数分别是二级叶子结点和三级叶子结点,如下图所示:
在这里插入图片描述
在运行命令的时候用.来隔开不同层级结点

python main.py pyouter_test.server.data.read_data_txt

在运行命令的时候可以在后面添加定制化参数如--cluster

python main.py pyouter_test.server.data.read_data_txt --cluster dev

通过options.cluster可以获得该参数的值"dev",还可以在src/options.py里面的def add_custom_arguments(parser: ArgumentParser)函数新增定制化参数,比如新增--input_file--output_file参数:

def add_custom_arguments(parser: ArgumentParser):
    ''' 用户定制化参数选项
    
    * --cluster: 开发环境还是线上环境
    '''

    parser.add_argument(
        "--cluster",
        dest="cluster",
        help="cluster dev or pro",
        default="dev",
        nargs='?',
        type=str,
        metavar="CLUSTER"
    )

    parser.add_argument(
        "--input_file",
        dest="input_file",
        help="input_file path",
        default="",
        nargs='?',
        type=str,
        metavar="INPUT_FILE"
    )

    parser.add_argument(
        "--output_file",
        dest="output_file",
        help="output_file path",
        default="",
        nargs='?',
        type=str,
        metavar="OUTPUT_FILE"
    )

执行以下命令的时候可以给input_fileoutput_file参数赋值

python main.py pyouter_test.server.data.read_data_txt --cluster dev --input_file './data/data_txt' --output_file './data/res.txt'

值得注意的是,无论是命令行输入的相对路径还是函数里面实现的路径都是针对/src/main.py的相对路径。

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值