2011-06-07 20 views
6

我創建一個小的Python腳本來管理不同類型的服務器(FTP,HTTP,SSH等)的Python ArgParse Subparsers並鏈接到正確的函數

在每個類型的服務器,我們可以執行不同類型的動作(部署,配置,檢查等)

我有一個基Server類,然後爲每個類型的服務器的一個單獨的類從該繼承:

class Server: 
    ... 
    def check(): 
     ... 

class HTTPServer(Server): 
    def check(): 
     super(HTTPServer, self).check() 
     ... 
class FTPServer(Server): 
    def check(): 
     super(FTPServer, self).check() 
     ... 

的樣本命令行可能成爲:

my_program deploy http 

在命令行中,兩個強制性參數,我們需要的是:

  1. 要執行的操作
  2. 類型服務器創建的/管理

以前,我是用​​和store操作,並使用dict將命令行選項與實際的類和函數名稱進行匹配。例如:

types_of_servers = { 
    'http': 'HTTPServer', 
    'ftp': 'FTPServer', 
    ... 
} 

valid_operations = { 
    'check': 'check', 
    'build': 'build', 
    'deploy': 'deploy', 
    'configure': 'configure', 
    'verify': 'verify', 
} 

(在我的實際代碼,valid_operations是不是很幼稚的1:1映射。)

,然後使用相當可怕的代碼來創建正確類型的對象,並調用正確的課程。

然後我想我會使用argparse的subparsers功能來代替它。所以我做了每個操作(檢查,構建,部署等)一個subparser

通常,我可以將每個子命令鏈接到一個特定的函數,並讓它調用它。但是,我不想僅僅調用通用的check()函數 - 我需要創建正確類型的對象第一個,然後調用該對象內的相應函數。

有沒有好的或pythonic的方式來做到這一點?最好是一個不涉及大量硬編碼,或者設計得很糟的if/else循環?

+0

你試過[面料](http://fabfile.org/ ) - 簡化使用SSH進行應用程序部署或系統管理任務的命令行工具? – jfs 2011-09-01 16:52:36

回答

2

如果您在使用一個子分析器每個命令我會做這樣的事情設置。使用argparse的類型支持來調用一個函數,該函數查找要實例化並返回的類。

然後用GETATTR()動態調用方法在該實例上

import argparse 

class Server: 
    def check(self): 
     return self.__class__.__name__ 

class FooServer(Server): 
    pass 

class BarServer(Server): 
    pass 


def get_server(server): 
    try: 
     klass = globals()[server.capitalize()+'Server'] 
     if not issubclass(klass, Server): 
      raise KeyError 

     return klass() 
    except KeyError: 
     raise argparse.ArgumentTypeError("%s is not a valid server." % server) 


if __name__ == '__main__': 
    parser = argparse.ArgumentParser() 
    subparsers = parser.add_subparsers(dest='command') 

    check = subparsers.add_parser('check') 
    check.add_argument('server', type=get_server) 

    args = parser.parse_args() 

    print getattr(args.server, args.command)() 

輸出看起來是這樣的:

$ python ./a.py check foo 
FooServer 
$ python ./a.py check bar 
BarServer 
$ python ./a.py check baz 
usage: a.py check [-h] server 
a.py check: error: argument server: baz is not a valid server. 
0

您可以在字典中使用對象本身。

#!/usr/bin/python 

class Server: 
    def __init__(self): 
     pass 

    def identify(self): 
     print self.__class__.__name__ 

    def check(self): 
     raise SomeErrorBecauseThisIsAbstract 

class HttpServer(Server): 

    def check(self, args): 
     if self.verify_http_things(): 
      return True 
     else: 
      raise SomeErrorBecauseTheCheckFailed 
    pass 

class FtpServer(Server): 

    def check(self, args): 
     if self.verify_ftp_things(): 
      return True 
     else: 
      raise SomeErrorBecauseTheCheckFailed 
    pass  


if __name__ == '__main__': 


    # Hopefully this edit will make my intent clear: 

    import argparse 
    parser = argparse.ArgumentParser(description='Process some server commands') 
    parser.add_argument('-c', dest='command') 
    parser.add_argument('-t', dest='server_type') 
    args = parser.parse_args() 

    servers = { 
     'http': HttpServer, 
     'ftp': FtpServer 
    } 

    try: 
     o = servers[args.server_type]() 
     o.__call__(args.command) 
    except Exception, e: 
     print e 
+0

謝謝=)。然而,我的問題更多的是事物的argparse/sub-command方面 - 如何將給定的命令行與正確的對象類型中的正確函數關聯起來。另外,如果它只是一個類,它會很容易,但是,我需要先創建適當的類類型,然後鏈接到該類中的函數,全部使用argparse的子命令。 – victorhooi 2011-06-07 10:34:09

0

這應該工作(但手動映射將在我看來,更直接):

import argparse 

class Proxy: 
    def __getattr__(thing): 
     def caller (type): 
      if type: 
       server_object = # get instance of server with right type 
       return getattr(server_object, thing)() 
     return caller 

parser = argparse.ArgumentParser() 

entry_parser.add_argument('--server_type', dest='server_type', required=True,choices=['http', 'ftp', 'ssh'],) 

subparser = parser.add_subparsers(dest='operation') 
for operation in ['check', 'build', 'deploy', 'configure', 'verify']: 
    entry_parser = subparser.add_parser(operation) 
    entry_parser.set_defaults(func=getattr(Proxy, command)) 

options = parser.parse_args() 

# this will call proxy function caller with type argument 
options.func(options.server_type)