用Python扩展命令行工具来学习编写插件(1)


本文将创建插件来扩展这些命令行工具,从而把它们提高到更高水平。插件和命令行工具都提供了扩展现有代码功能的简便方法。它们结合在一起可以形成非常强大的工具。

为了开始编写插件,我们要使用我编写的开放源码 Python 包 pathtool,这个库使用生成器操作文件系统并产生一个文件对象。这个库允许开发人员编写自己的过滤器来扩展它,过滤器对文件对象做一些处理,然后返回结果。

实际的 Python 模块代码比较长,不适合在本文中给出,所以只介绍开发人员实际使用的 API 片段:

清单 1. pathtool API

def path(fullpath, pattern="*", action=(lambda rec: print_rec(rec))):
"""This takes a path, a shell pattern, and an action callback
This function uses the slower pathattr function which calculates checksums
"""
for rec in pathattr(fullpath):
for new_record in match(pattern, rec): #applies filter
action(new_record) #Applies lambda callback to generator object


看一下这个示例,可以看出这个路径函数有一个必需的路径位置参数,还有一个可选的模式关键字参数和一个可选的动作关键字参数称为 lambda 回调函数)。路径的默认回调函数仅仅输出文件名。开发人员只需要执行 easy_install 命令。关于使用 easy_install 命令的信息参见 参考资料。然后执行以下命令导入这个模块并调用函数:

from pathtool import path
 path("/tmp", pattern="*.mp3", action=(lambda rec: print_rec(rec)))

注意:本文提供了 pathtool 的源代码。这个示例的关键点是使用 lambda。在参考资料 中可以找到关于 lambda 的 Python 教程,但是简单地说,lambda 是让一个函数“调用” 另一个函数的简便方法。

编写一个可插入的命令行工具

我们已经基本了解了如何使用这个包含回调函数的路径操作库,现在要编写一个可以用插件扩展的命令行工具。先看一下完成后的版本,然后分析其组成部分:

清单 2. 带插件的命令行工具

#!/usr/bin/env python
# encoding: utf-8
"""
pathtool-cli.py 0.1
A commandline tool for walking a filesystem.
Takes Action callback plugins in a plugin directory
action=(lambda rec: print_rec(rec))
"""
from pathtool import path
import optparse
import re
import os
import sys
try:
plugin_available = True
from plugin import *
from plugin import __all__ #note this is the registered plugin list
except ImportError:
plugin_available = False

def path_controller():
descriptionMessage = """
A command line tool for walking a filesystem.
Takes callback 'Action' functions as plugins.

example: pathtool_cli /tmp print_path_ext
"""
p = optparse.OptionParser(description=descriptionMessage,
prog='pathtool',
version='pathtool 0.1.1',
usage= '%prog [starting directory][action]')
p.add_option('--pattern', '-p',
help='Pattern Match Examples: *.txt, *.iso, music[0-5].mp3
plain number defaults to * or match all. 
Uses UNIX standard wildcard syntax.',
default='*')
p.add_option('--list', '-l',
action="store_true",
help='lists available action plugins',
default=False)

options, arguments = p.parse_args()
if options.list:
try:
print "Action Plugins Available:"
if plugin_available:
for p in __all__:
print p
finally:
sys.exit(0)

if len(arguments) == 2:
fullpath = arguments[0]
try:
action_plugin = eval(arguments[1]) 
#note we expect the plugin author to write a method with our naming convention
#path(fullpath,options.pattern,action=(lambda rec: move_to_tmp.plugin(rec)))
path(fullpath, options.pattern,action=(lambda rec: action_plugin.plugin(rec)))
except NameError:
sys.stderr.write("Plugin Not Found")
sys.exit(1)
else:
print p.print_help()
def main():
path_controller()
if __name__ == '__main__':
main()


运行这个示例会产生以下输出:

# python pathtool_cli.py
Usage: pathtool [starting directory][action]
 A command line tool for walking a filesystem.Takes callback 'Action'
functions as plugins. example: pathtool_cli /tmp print_path_ext
Options:
 --version show program's version number and exit
 -h, --helpshow this help message and exit
 -p PATTERN, --pattern=PATTERN
Pattern Match Examples: *.txt, *.iso, music[0-5].mp3
plain number defaults to * or match all.
Uses UNIX standard wildcard syntax.
 -l, --listlists available action plugins

在这个命令的输出中可以看到,这个工具需要一个完整路径,然后是一个“动作”。动作是开发人员创建的一个插件。我增加了一个命令行列表选项,让这个命令行工具的用户可以看到可用的插件。看一下它的输出:

# python pathtool_cli.py -l
Action Plugins Available:
move_to_tmp
print_file_path_ext

即使不太了解这个工具的工作原理,也能够通过动作的名称猜出它会执行哪些操作。我编写的 print_file_path_ext 动作仅仅输出路径、文件名和扩展名,运行它,看看它的输出:

# python pathtool_cli.py /tmp print_file_path_ext
/tmp/foo0.txt | foo0.txt | .txt
/tmp/foo1.txt | foo1.txt | .txt
/tmp/foo10.txt | foo10.txt | .txt
/tmp/foo2.txt | foo2.txt | .txt
/tmp/foo3.txt | foo3.txt | .txt
/tmp/foo4.txt | foo4.txt | .txt
/tmp/foo5.txt | foo5.txt | .txt
/tmp/foo6.txt | foo6.txt | .txt
/tmp/foo7.txt | foo7.txt | .txt
/tmp/foo8.txt | foo8.txt | .txt
/tmp/foo9.txt | foo9.txt | .txt

我使用 touch foo{0..10}.txt 创建了十一个临时文件,现在这个命令行工具使用它找到的一个插件显示完整路径、文件名和扩展名以 “|” 字符分隔)。


评论关闭