Raymond Hettinger <[email protected]> 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 <[email protected]>
<https://bugs.python.org/issue43468>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com