6

さまざまなクラスのサーバー (FTP、HTTP、SSH など) を管理するための小さな Python スクリプトを作成しています。

サーバーの種類ごとに、さまざまな種類のアクション (デプロイ、構成、チェックなど) を実行できます。

基本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

コマンドラインから、必要な 2 つの必須引数は次のとおりです。

  1. 実行する操作
  2. 作成/管理するサーバーの種類

以前は、 argparseandstore操作を使用し、aを使用し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機能を使用することにしました。そこで、各操作 (チェック、ビルド、デプロイなど) をサブパーサーにしました。

通常、各サブコマンドを特定の関数にリンクして、それを呼び出すことができます。ただし、ジェネリック関数を呼び出すだけでは不十分です。最初check()に正しいタイプのオブジェクトを作成してから、そのオブジェクト内で適切な関数を呼び出す必要があります。

これを行うための良い、またはpythonicな方法はありますか? できれば、多くのハードコーディングを含まないもの、または不適切に設計された if/else ループですか?

4

3 に答える 3

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.
于 2011-09-01T02:09:32.693 に答える
0

dict でオブジェクト自体を使用することもできます。

#!/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
于 2011-06-07T09:48:02.120 に答える
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)
于 2011-06-07T12:22:42.983 に答える