歡迎您光臨本站 註冊首頁

用 Python 編寫插件

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  本文將創建插件來擴展這些命令行工具,從而把它們提高到更高水平。插件和命令行工具都提供了擴展現有代碼功能的簡便方法。它們結合在一起可以形成非常強大的工具。

為了開始編寫插件,我們要使用我編寫的開放源碼 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, --help            show 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, --list            lists 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 創建了十一個臨時文件,現在這個命令行工具使用它找到的一個插件顯示完整路徑、文件名和擴展名(以 “|” 字元分隔)。

簡單的插件體系結構

到目前為止,我只討論了如何使用這個工具,還沒有解釋這些插件的工作原理。先看看這個模塊頂部的導入語句:

    plugin_available = True      from plugin import *      from plugin import __all__ #note this is the registered plugin list  except ImportError:      plugin_available = False  

這個導入語句揭示了這個極其簡單的插件體系結構的秘密。一般情況下,Python 官方文檔不鼓勵使用 “from package import *” 語法,但是如果有合理理由的話(比如編寫插件),可以這樣做。插件作者負責在插件目錄中的 __init__.py 文件中創建一個條目。這個條目應該像下面這樣:

"""Lists all of the importable plugins"""  __all__ = ["move_to_tmp", "print_file_path_ext"]       

通過創建這個條目,可以以 * 的形式導入包(或目錄)中的所有模塊。接下來,導入實際的 __all__ 列表,向用戶顯示可用的插件。最後,還需要一行代碼。因為直到運行之前命令行工具並不知道要使用哪個插件動作,所以要使用 eval 把命令行上的動作字元串轉換為一個可調用的函數,如下所示:

action_plugin = eval(arguments[1])   

在一般情況下,應該極其謹慎地使用 eval,但是在這裡通過 eval 告訴工具使用哪些插件方法是合理的。

插件示例分析

既然已經了解了這個插件體系結構的工作原理,就來看看實際的插件。注意,為了讓這個體系結構發揮作用,需要在當前工作目錄或 Python site-packages 目錄中創建一個插件目錄。我們要討論的插件稱為 print_file_path_ext.py,它包含一個稱為 plug-in 的方法。這是插件開發人員必須滿足的 API 要求。


清單 3. 插件示例
#!/usr/bin/env python  # encoding: utf-8  """  prints path, name, ext, plugin  """    def plugin(rec, verbose=True):      """Moves matched files to tmp directory"""      path = rec["path"]      filename = rec["filename"]      ext = rec["ext"]      print "%s | %s | %s" % (path, filename, ext)  

這個插件非常簡單。它有一個 rec 參數,這個參數是 pathtool 模塊生成的詞典。這個詞典包含以下 API:

{"path": path, "filename": file, "ext": ext, "size": size,  "unique_id": unique_id, "mtime": mtime, "ctime": ctime}  

在這個示例中,每當調用它時,使用詞典的鍵輸出特定文件對象的值。插件作者可以編寫許多更有用的動作,比如對文件進行轉換、重命名、存檔等等。

結束語

本文介紹了一個非常簡單的插件體系結構,可以通過它用 Python 擴展命令行工具。但是,應該注意幾點。首先,可以通過 easy_install 使用一個更高級的插件系統(參見參考資料)。這個插件系統允許用戶創建 “入口點” 來為工具定義插件。第二,我們的命令行工具只允許一個 “動作” 插件。可以修改這個命令行工具,讓它能夠接受數量不限的 “鏈式” 回調動作,這留給讀者作為練習。

關於創建鏈式插件還有一個問題:設計必須考慮到使用的 API 的性質。在我們的示例中,以生成器作為基礎。為了讓這個工具能夠把插件 “鏈接” 在一起,各個插件必須完成本身的工作,然後生成詞典記錄。我希望本文能夠鼓勵您為命令行工具編寫自己的插件。 (責任編輯:A6)



[火星人 ] 用 Python 編寫插件已經有491次圍觀

http://coctec.com/docs/linux/show-post-69079.html