1

我正在玩contract.py,Terrence Way 的 Python 合同设计参考实现。当违反合同(前置条件/​​后置条件/不变量)时,实现会引发异常,但如果有多个与方法关联的合同,它不会为您提供快速识别哪个特定合同失败的方法。

例如,如果我以circbuf.py为例,并通过传入否定参数来违反前提条件,如下所示:

circbuf(-5)

然后我得到一个看起来像这样的回溯:

Traceback (most recent call last):
  File "circbuf.py", line 115, in <module>
    circbuf(-5)
  File "<string>", line 3, in __assert_circbuf___init___chk
  File "build/bdist.macosx-10.5-i386/egg/contract.py", line 1204, in call_constructor_all
  File "build/bdist.macosx-10.5-i386/egg/contract.py", line 1293, in _method_call_all
  File "build/bdist.macosx-10.5-i386/egg/contract.py", line 1332, in _call_all
  File "build/bdist.macosx-10.5-i386/egg/contract.py", line 1371, in _check_preconditions
contract.PreconditionViolationError: ('__main__.circbuf.__init__', 4)

我的预感是 PreconditionViolationError (4) 中的第二个参数是指 circbuf 中的行号。包含断言的初始化文档字符串:

def __init__(self, leng):
    """Construct an empty circular buffer.

    pre::
        leng > 0
    post[self]::
        self.is_empty() and len(self.buf) == leng
    """

但是,必须打开文件并计算文档字符串的行号是一件很痛苦的事情。有没有人有更快的解决方案来确定哪个合同失败了?

(请注意,在这个例子中,有一个前提条件,所以很明显,但多个前提条件是可能的)。

4

2 回答 2

1

在不修改他的代码的情况下,我认为您不能,但是由于这是python...

如果您寻找他向用户提出异常的位置,我认为可以将您正在寻找的信息推送到其中......我不希望您能够获得任何追溯不过更好,因为代码实际上包含在注释块中,然后进行处理。

代码非常复杂,但这可能是一个需要查看的块 - 也许如果你转储一些参数,你可以弄清楚发生了什么......

def _check_preconditions(a, func, va, ka):
    # ttw006: correctly weaken pre-conditions...
    # ab002: Avoid generating AttributeError exceptions...
    if hasattr(func, '__assert_pre'):
        try:
            func.__assert_pre(*va, **ka)
        except PreconditionViolationError, args:
            # if the pre-conditions fail, *all* super-preconditions
            # must fail too, otherwise
            for f in a:
                if f is not func and hasattr(f, '__assert_pre'):
                    f.__assert_pre(*va, **ka)
                    raise InvalidPreconditionError(args)
            # rr001: raise original PreconditionViolationError, not
            # inner AttributeError...
            # raise
            raise args
            # ...rr001
    # ...ab002
    # ...ttw006
于 2009-06-20T05:01:22.487 回答
1

这是一个老问题,但我不妨回答一下。我添加了一些输出,您将在评论 #jlr001 中看到它。将下面的行添加到您的 contract.py 中,当它引发异常时,它将显示文档行号和触发它的语句。仅此而已,但它至少会阻止您猜测是哪个条件触发了它。

def _define_checker(name, args, contract, path):
    """Define a function that does contract assertion checking.

    args is a string argument declaration (ex: 'a, b, c = 1, *va, **ka')
    contract is an element of the contracts list returned by parse_docstring
    module is the containing module (not parent class)

    Returns the newly-defined function.

    pre::
        isstring(name)
        isstring(args)
        contract[0] in _CONTRACTS
        len(contract[2]) > 0
    post::
        isinstance(__return__, FunctionType)
        __return__.__name__ == name
    """
    output = StringIO()
    output.write('def %s(%s):\n' % (name, args))
    # ttw001... raise new exception classes
    ex = _EXCEPTIONS.get(contract[0], 'ContractViolationError')
    output.write('\tfrom %s import forall, exists, implies, %s\n' % \
                (MODULE, ex))
    loc = '.'.join([x.__name__ for x in path])
    for c in contract[2]:
        output.write('\tif not (')
        output.write(c[0])
        # jlr001: adding conidition statement to output message, easier debugging
        output.write('): raise %s("%s", %u, "%s")\n' % (ex, loc, c[1], c[0]))
    # ...ttw001

    # ttw016: return True for superclasses to use in preconditions
    output.write('\treturn True')
    # ...ttw016

    return _define(name, output.getvalue(), path[0])
于 2012-06-13T14:39:08.837 回答