Python 命令行应用 - 创建项目结构

·217 字·2 分钟
Python command-line
n3xtchen
作者
n3xtchen
Sharing Funny Tech With You

我喜欢使用 Python 创建命令行应用,它写起来比 Bash 脚本更有逻辑直观。

Python 有很多库提供给你解析命令行参数和运行其它 Shell 命令,同时你还能充分利用强大的面向对象语言的优势;你还可以使用 Python 的单元测试来帮助你检验和注释你的应用。

你可以在 github 找到这个教程的 demo。

结构化你的应用 #

从我的经验来看,Python最好的目录结构就是将可执行脚本放到 bin 目录中,你的项目放到项目名目录下。这样,你可以分离你的核心功能,保持可复用性。这也是其它类型应用的创建标准规则。

project 目录下,你应该使用 main.py 作为应用的主要访问入口。你的通用函数应该放到 lib 目录下,测试脚本放在 tests 目录下。结构如下:

├── README.md
├── bin
│   ├── project
│   └── project.bat
└── project
    ├── lib
    ├── main.py
    └── tests

你的应用可以这样执行:

$ bin/project <parameters>

分离参数,shell 命令和功能函数 #

和所有面相对象编程一样,你应该遵循 关注点分离(SoC)原则。由于用 Python 读取命令行参数,处理选项和执行其他 Shell 命令是在它方便了,导致时常忽略了这个原则。

解析命令行参数 #

创建一个定义和收集命令行参数的类。Python 提供了 argparse(原教程使用的旧库 optparse,目前已经被 Python 2.7 弃用),这样你可以非常容易定义配置和行为(介绍 ArgumentParser 的基础用法):

from argparse import ArgumentParser

usage = 'bin/project'
self.parser = ArgumentParser(usage=usage)
self.parser.add_argument('-x',
    '--example',
    default='example-value',
    dest='example',
    help='An example option')

现在你已经创建了一个解释器,它通过执行 bin/project -x <target value> 或者 bin/project --example <target value>,读取目标值到 example 变量中。

执行其他 Shell 命令 #

如果你需要创建一个依赖于其他 Shell 命令的应用的时候,你应该从它的类中把命令抽离出来。这样,你的核心功能可以在不同环境下被复用,你也更容易收集外部代码产生的日志,错误和异常。我推荐你使用 Pythonsubprocess 来执行外部命令。

创建一个类用来处理进程执行和异常(见 process-class)。

核心功能 #

project/lib/project.py 中,你可以实现核心功能。由于这是个例子,我只包含接受参数,并通过 Process-class 执行 date 命令(见 porject.py)。

运行 #

bin/project 可执行文件中调用你项目的 maim.py

#!/bin/bash

BINPATH=`dirname $0`
python "$BINPATH/../project/main.py" $@

不要忘了为它设置可执行权限:

$ chmod 755 bin/project

main.py 中收集命令行参数值和运行你的应用。

import sys

from lib import Project
from lib import Options

if __name__ == '__main__':
    options = Options()
    opts, args = options.parse(sys.argv[1:])
    v = Project(opts)

    print v.date()

你现在可以执行你的应用了:

$ bin/project <arguments>

测试 #

介于文章的主题,这里我不再强调测试的重要性。简单扼要的说,单元测试可以引导你的开发,验证功能,执行应用的行为。

把测试添加到 project/tests 目录中。我推荐使用 nose 来运行你的测试。

测试命令行参数 #

import unittest

from lib import Options

class TestCommandLineArguments(unittest.TestCase):
    def setUp(self):
        self.options = Options()

    def test_defaults_options_are_set(self):
        opts, args = self.options.parse()
        self.assertEquals(opts.example, 'example-value')

运行测试 #

$ nosetests
.....
Ran 5 tests in 0.054s

OK

引用自 https://coderwall.com/p/lt2kew/python-creating-your-project-structure