Skip to content

Python 简单后台项目的脚手架

Published: at 00:00

说明

近期写了一个简单的项目,在后台运行获取网上的期货数据并保存到相应的数据库里。由于之前工作很多这种简单的类似调用接口或攫取数据的项目都是用 Python 来写,因此这次也继续用 Python 写。但是这次更换了几个包,此份文档简单来说明一下。

依赖的包

项目结构

$ tree -L 2
.
├── README.md
├── config
   └── config.toml.                            # 配置文件
├── log                                         # 日志目录
├── app_demo                                    # 程序目录
   ├── __init__.py
   ├── config.py                               # 加载配置信息代码
   ├── container.py.                           # 容器类
   ├── exceptions.py                           # 异常类
   ├── log.py                                  # 日志程序
   └── main.py                                 # 主程序
├── setup.py                                    # 安装程序
└── venv                                        # 虚拟环境
    ├── bin
    ├── lib
    └── pyvenv.cfg

部分代码示例

1、main.py

import time
import arrow
import numpy as np
import click
import sqlalchemy
from sqlalchemy import text
from app_demp.container import Container

config = None
log = None
db = None

@click.command()
@click.option('--config_file', default='./config.toml', help='Specify config file')
@click.option('--start_date', default=None, help='Specify start date')
@click.option('--end_date', default=None, help='Specify end date')
def main(config_file, start_date, end_date):
    global config, log, db
    print("Load config file: {}".format(config_file))
    container = Container(config_file)
    config = container.get_config()
    log = container.get_logger()
    log.info("Load config file: {}".format(config_file))
    db = container.get_db()

    if start_date is None or end_date is None:
        start_date = arrow.now().format('YYYYMMDD')
        end_date = start_date

    log.info("Date Info, start date: {}, end date: {}".format(start_date, end_date))

    run(start_date, end_date)


if __name__ == '__main__':
    main()

2、container.py


from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker


class Container(object):
    """
    容器类
    """
    def __init__(self, config_file):
        """

        """        self.config_file = config_file
        self.config = None
        self.logger = None
        self.db = None

    def get_config(self):
        """
        获取指定配置信息
        :return:
        """        if self.config is None:
            from app_demo.config import get_config
            self.config = get_config(self.config_file)
        return self.config

    def get_logger(self):
        """
        获取日志对象
        :return:
        """
        if self.config is None:
            self.get_config()
        if self.logger is None:
            from app_demo.log import get_logger
            self.logger = get_logger(self)
        return self.logger

    def get_db(self):
        if self.config is None:
            self.get_config()

        if self.db is None:
            dsn = "mysql+mysqlconnector://{user}:{password}@{host}:{port}/{database}?auth_plugin=mysql_native_password".format(
                    user=self.get_config().get('db').get('user'),
                    password=self.get_config().get('db').get('password'),
                    host=self.get_config().get('db').get('host'),
                    port=self.get_config().get('db').get('port'),
                    database=self.get_config().get('db').get('database'),
                )

            engine = create_engine(dsn, echo=False)
            db_session = sessionmaker(bind=engine, autoflush=True, expire_on_commit=True)
            self.db = db_session()

        return self.db

3、config.py

import toml

from app_demo.exceptions import *


def get_config(config_file):
    if config_file is not None:
        with open(config_file) as config_file_handler:
            config = toml.loads(config_file_handler.read())

            if not all(key in config for key in ['log']):
                raise ConfigException('Config error, config file: {file}'.format(file=config_file))

            if not (isinstance(config.get('db'), dict) and all(key in config.get('db') for key in
                                                               ['host', 'port', 'database', 'user', 'password'])):
                raise ConfigException('Config error, config file: {file}'.format(file=config_file))
        return config
    else:
        raise Exception('Can not retrieve config file')

4、log.py

from loguru import logger


def get_logger(container):
    log_config = container.get_config().get('log')

    logger.add(
        log_config.get('filename'),
        rotation=log_config.get('rotation_size'),
        retention=log_config.get('retention'),
        compression=log_config.get('compression'),
    )

    return logger

结论

软件工程方面,软件开发方法学的泰山北斗 Kent Beck 说过一句至理名言: “Make it Work, Make it Right, Make it Fast.”,最重要是先 Make it Work。简单点,大家可以参考这个脚手架先把项目做起来,后面再慢慢完善。