Raymond Hettinger <raymond.hettin...@gmail.com> added the comment:
Here's the latest effort: --------------------------------------------------------------- def __get__(self, instance, owner=None): if instance is None: return self if self.attrname is None: raise TypeError( "Cannot use cached_property instance without calling __set_name__ on it.") try: cache = instance.__dict__ except AttributeError: # not all objects have __dict__ (e.g. class defines slots) msg = ( f"No '__dict__' attribute on {type(instance).__name__!r} " f"instance to cache {self.attrname!r} property." ) raise TypeError(msg) from None # Quickly and atomically determine which thread is reponsible # for doing the update, so other threads can wait for that # update to complete. If the update is already done, don't # wait. If the updating thread is reentrant, don't wait. key = id(self) this_thread = get_ident() with self.updater_lock: val = cache.get(self.attrname, _NOT_FOUND) if val is not _NOT_FOUND: return val wait = self.updater.setdefault(key, this_thread) != this_thread # ONLY if this instance currently being updated, block and wait # for the computed result. Other instances won't have to wait. # If an exception occurred, stop waiting. if wait: with self.cv: while cache.get(self.attrname, _NOT_FOUND) is _NOT_FOUND: self.cv.wait() val = cache[self.attrname] if val is not _EXCEPTION_RAISED: return val # Call the underlying function to compute the value. try: val = self.func(instance) except Exception: val = _EXCEPTION_RAISED # Attempt to store the value try: cache[self.attrname] = val except TypeError: # Note: we have no way to communicate this exception to # threads waiting on the condition variable. However, the # inability to store an attribute is a programming problem # rather than a runtime problem -- this exception would # likely occur early in testing rather than being runtime # event triggered by specific data. msg = ( f"The '__dict__' attribute on {type(instance).__name__!r} instance " f"does not support item assignment for caching {self.attrname!r} property." ) raise TypeError(msg) from None # Now that the value is stored, threads waiting on the condition # variable can be awakened and the updater dictionary can be # cleaned up. with self.updater_lock: self.updater.pop(key, None) cache[self.attrname] = _EXCEPTION_RAISED self.cv.notify_all() if val is _EXCEPTION_RAISED: raise return val ---------- _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue43468> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com