2017-08-28 51 views
0

我想在一个web应用程序中使用PyContracts,所以我有很多自定义类被传递,我只是想与其他更传统的参数类型一起检查。为了清洁和强制文档,我想使用合同编程(PyContracts)来完成此任务。引用本地pycontract类型

当我通过名称引用本地可见的类时,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"),所以我肯定需要使用字符串定义的合同格式。我如何用本地类型完成此任务,并尽可能少地侵入我的其他代码库?

回答

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(). 
...