2017-08-28 1 views
0

J'essaie d'utiliser PyContracts dans une application Web. J'ai donc beaucoup de classes personnalisées que je veux simplement vérifier avec d'autres types d'arguments plus traditionnels. Je voudrais utiliser la programmation contractuelle (PyContracts) pour accomplir ceci, pour la propreté et la documentation forcée.Type local de référence dans pycontract

Lorsque je référence un nom de classe localement visible, PyContracts ne semble pas connaître le type. Par exemple:

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) 

génère l'erreur suivante:

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

Je sais que je peux utiliser new_contract aux noms sur mesure de définir et de les lier à des classes, mais c'est beaucoup de tracas à faire pour chaque type . Je veux utiliser la syntaxe docstring pour PyContracts si possible, et je dois absolument utiliser le format de contrat défini par la chaîne puisque j'utilise la logique de type booléenne ("None|str|SomeClass"). Comment puis-je accomplir cela avec des types locaux et une intrusion minimale dans le reste de mon code?

Répondre

0

J'ai piraté ensemble un décorateur magique qui ajoute des types avant de créer le contrat. Pour toute personne qui est intéressé, il semble fonctionner, mais il est probablement assez lent si vous définissez un grand nombre de fonctions:

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 

Et quelques séries de tests:

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