1

我正在尝试在 Web 应用程序中使用 PyContracts,所以我有很多自定义的类被传递,我只是想与其他更传统的参数类型一起进行类型检查。为了清洁和强制文档,我想使用合同编程(PyContracts)来实现这一点。

当我按名称引用本地可见类时,PyContracts 似乎不知道该类型。例如:

from contracts import contract

class SomeClass:
    pass

@contract
def f(a):
    """

    :param a: Just a parameter
    :type a: SomeClass
    """
    print(a)

my_a = SomeClass()
f(my_a)

引发以下错误:

ContractSyntaxError: Unknown identifier 'SomeClass'. Did you mean 'np_complex64'? (at char 0), (line:1, col:1)

我知道我可以使用 new_contract 来自定义名称并将它们绑定到类,但是对于每种类型来说这都是很多麻烦。如果可能的话,我想使用 PyContracts 的 docstring 语法,而且我肯定需要使用字符串定义的合同格式,因为我使用的是布尔类型逻辑 ( "None|str|SomeClass")。如何使用本地类型和对代码库其余部分的最小入侵来实现这一点?

4

1 回答 1

0

我拼凑了一个魔法装饰器,它在创建合同之前添加了类型。对于任何感兴趣的人来说,它似乎可以工作,但是如果您定义大量函数,它可能会很慢:

def magic_contract(*args, **kwargs):
    # Check if we got called without arguments, just the function
    func = None
    if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
        func = args[0]
        args = tuple()

    def inner_decorator(f):
        for name, val in f.__globals__.items():
            if isinstance(val, type):
                new_contract(name, val)
        return contract(*args, **kwargs)(f)

    if func:
        return inner_decorator(func)
    return inner_decorator

还有一些测试运行:

In [3]: class SomeClass:
   ...:     pass
   ...:

In [4]: @magic_contract
   ...: def f(a):
   ...:     """
   ...:
   ...:     :param a: Some parameter
   ...:     :type a: None|SomeClass
   ...:     """
   ...:     print(a)
   ...:

In [5]: f(None)
None

In [6]: f(SomeClass())
<__main__.SomeClass object at 0x7f1fa17c8b70>

In [7]: f(2)
...
ContractNotRespected: Breach for argument 'a' to f().
...

In [8]: @magic_contract(b='int|SomeClass')
   ...: def g(b):
   ...:     print(type(b))
   ...:

In [9]: g(2)
<class 'int'>

In [10]: g(SomeClass())
<class '__main__.SomeClass'>

In [11]: g(None)
...
ContractNotRespected: Breach for argument 'b' to g().
...
于 2017-08-28T18:06:49.120 回答