decorator to cache the result of a function until a field changes

def dirty(field,ttl=None):
  "decorator to cache the result of a function until a field changes"
  if ttl is not None: raise NotImplementedError('pg.dirty ttl feature')
  def decorator(f):
    @functools.wraps(f)
    def wrapper(self,*args,**kwargs):
      # warning: not reentrant
      d=self.dirty_cache[field] if field in self.dirty_cache else self.dirty_cache.setdefault(field,{})
      return d[f.__name__] if f.__name__ in d else d.setdefault(f.__name__,f(self,*args,**kwargs))
    return wrapper
  return decorator