2011-06-07 58 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)