【问题标题】:Python ArgParse Subparsers and linking to the correct functionPython ArgParse 子解析器并链接到正确的函数
【发布时间】:2011-09-09 21:43:47
【问题描述】:

我正在创建一个小的 Python 脚本来管理不同类别的服务器(FTP、HTTP、SSH 等)

在每种类型的服务器上,我们可以执行不同类型的操作(部署、配置、检查等)

我有一个基本的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. 要创建/管理的服务器类型

以前,我使用argparsestore 操作,并使用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 循环?

【问题讨论】:

  • 您是否尝试过Fabric - 用于简化 SSH 用于应用程序部署或系统管理任务的命令行工具?

标签: python parameters command-line-arguments argparse


【解决方案1】:

你可以在字典中使用对象本身。

#!/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

【讨论】:

  • 谢谢 =)。但是,我的问题更多的是关于 argparse/sub-command 方面的问题——如何将给定的命令行链接到正确对象类型中的正确函数。另外,如果它只是一个类,那会很容易,但是,我需要先创建适当的类类型,然后链接到其中的函数,所有这些都使用 argparse 的子命令。
【解决方案2】:

这应该可行(但我认为手动映射会更直接):

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)

【讨论】:

    【解决方案3】:

    如果您设置为每个命令使用子解析器,我会做这样的事情。使用 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.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-31
      • 2013-06-09
      • 2013-02-27
      • 2013-12-21
      • 2013-10-07
      • 2018-04-23
      • 2021-05-11
      • 2020-12-04
      相关资源
      最近更新 更多