2017-02-23 1 views
2

je suis en train de prendre des exceptions à l'aide decorator pour une cached_propertyhttps://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76exception attrapant avec cached_property

https://pypi.python.org/pypi/cached-property

Je voudrais faire quelque chose aussi facile comme suit, mais cela ne fonctionne pas

from pprint import pprint 
import time 
from cached_property import cached_property 

class MyException(Exception): 
    pass 

def catch_my_exceptions(fn): 
    def wrapped(*args, **kwargs): 
     try: 
      return fn(*args, **kwargs) 
     except MyException as e: 
      cls = args[0] 
      err = 'Found error at {}: {}'.format(time.asctime(), e) 
      cls.error_msgs.append(err) 
      print(err) 
      return 
    return wrapped 

class Foo(object): 
    def __init__(self): 
     self.vars = {} 

    @cached_property 
    @catch_my_exceptions 
    def is_cache_working(self): 
     self.vars[time.asctime()] = True 
     time.sleep(3) 
     print('running cache runner') 
     return time.asctime() 

fo = Foo() 
for i in range(3): 
    print(fo.is_cache_working) 
    pprint(fo.vars) 


# This doesn't trigger caching 

running cache runner 
Thu Feb 23 21:45:15 2017 
{'Thu Feb 23 21:45:11 2017': True} 
running cache runner 
Thu Feb 23 21:45:18 2017 
{'Thu Feb 23 21:45:11 2017': True, 'Thu Feb 23 21:45:15 2017': True} 
running cache runner 
Thu Feb 23 21:45:21 2017 
{'Thu Feb 23 21:45:11 2017': True, 
'Thu Feb 23 21:45:15 2017': True, 
'Thu Feb 23 21:45:18 2017': True} 



# Current solution that works: 

Mon piratage autour de ceci est le suivant. Quelqu'un peut-il me suggérer un meilleur moyen. aussi comment pourrais-je passer une liste des exceptions à cette my_cached_decorator

import time 
from pprint import pprint 
from cached_property import cached_property 

class MyException(Exception): 
    pass 

class my_cached_property(cached_property): 
    def __init__(self, func): 
     super(self.__class__, self).__init__(func) 

    def __get__(self, obj, cls): 
     try: 
      super(self.__class__, self).__get__(obj, cls) 
     except MyException as e: 
      err = 'Found error at {}: {}'.format(time.asctime(), e) 
      print(err) 
      value = obj.__dict__[self.func.__name__] = None 
      return value 

class Foo(object): 
    def __init__(self): 
     self.vars = {} 

    @my_cached_property 
    def is_cache_working(self): 
     self.vars[time.asctime()] = True 
     time.sleep(3) 
     print('running cache runner') 
     raise MyException('fooobar') 
     return time.asctime() 

fo = Foo() 
for i in range(3): 
    print(fo.is_cache_working) 
    pprint(fo.vars) 

Répondre

0

Il est sans doute pas la meilleure solution, mais vous aurez accès à la fonction intérieure est revenu du décorateur à l'appelant et aussi à l'intérieur de la fermeture du décorateur.

Exemple:

def decorator(f): 
    def wrapper(*args, **kwargs): 
     try: 
      f(*args, **kwargs) 
     except Exception as e: 
      wrapper.__dict__.setdefault('errors', []).append(e) 
    return wrapper 

@decorator 
def raiser(): 
    raise Exception('Oh no!') 

> raiser() 
> raiser.errors 
[Exception('Oh no!')] 
0

Eh bien, je compris le problème, et il est dans la façon dont les travaux de cached_property. Pour le mettre en cache, il écrit la valeur sur l'instance sous le même nom que la fonction qu'il a enveloppée. Le problème est que le nom de la fonction qui l'enveloppe a le nom "wrapped", de votre décorateur. Donc, si vous avez accédé à fo.wrapped après le fo.is_cache_working initial, vous obtiendrez votre résultat en cache.

Il n'y a pas de moyen facile de mélanger les deux idées ensemble. La solution la plus simple consiste à écrire votre propre propriété mise en cache qui stocke la valeur sur elle-même:

class cached_property(object): 
    def __init__(self, func): 
     self.func = func 
     # you can store other function attributes here - such as __doc__ - if you want 
     self.values = {} 

    def __get__(self, instance, owner): 
     if instance in self.values: 
      return self.values[instance] 
     else: 
      value = self.values[instance] = self.func(instance) 
      return value