可以与语句一起使用以执行条件和完成

时间:2014-10-16 20:59:11

标签: python with-statement

我有一个非常常见的代码模式:

if not resource.hasInstallTag(tag="mysoftwareservice", version="5"):
    doX()
    doY()   
    frobnicate()   
    do()   
    installMySoftwareService()

    resource.recordInstallTag(tag="mysoftwareservice", version="5")

然而,在一个地方更新版本字符串并忘记在另一个地方执行此操作非常容易,或忘记忘记将调用添加到recordInstallTag。

我希望有类似以下的模式:

with InstallTag(resource, tag, version):
    doX()
    installTheThingThat(version)
    doY()   
    frobnicate()   

其中:

  • 如果资源已经将tag ==标签安装到版本==版本,则跳过内部语句块的内容(不会引发异常)
  • (仅)如果内部语句块无异常成功,则记录此版本的此项已安装到此资源。

例如:

class TagAlreadySetException(Exception):
    pass

class InstallTag(object):
    def __init__(self, resource, tag, version):
        self.resource = resource
        self.tag = tag
        self.version = version

    def __enter__(self):
        if self.resource.hasInstallTag(self.tag, self.version):
            # Prevent execution of statement block
            raise TagAlreadySetException()
        return None

    def __exit__(self, type, value, tb):
        if type==None:
            self.resource.recordInstallTag(self.tag, self.version)

不幸的是,虽然提高TagAlreadySetException会阻止执行语句块,但它也会继续将异常抛出调用堆栈。我想捕获TagAlreadySetException并处理它。我可以在没有额外额外机制的情况下执行此操作吗?

我意识到我可以尝试......最后。但是,我试图确保标签和项目只在应用模式时写入一次(以防止出现不一致)

1 个答案:

答案 0 :(得分:1)

这是一个整洁的小伙伴:

import contextlib

def manager_gen(resource, tag, version):
    def check():
        check.called = True
        if resource.hasInstallTag(tag, version):
            raise TagAlreadySetException

    check.called = False

    try:
        yield check
    except TagAlreadySetException:
        pass
    finally:
        if not check.called:
            raise RuntimeError("check wasn't called!")

InstallTag = contextlib.contextmanager(manager_gen)

你会像这样使用它:

with InstallTag as checker:
    checker()
    raz_the_jazz_and_other_neat_statements()

如果您忘记运行检查,它会在块结束时向您大喊(可能有用也可能没用......)