This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author rhettinger
Recipients carljm, graingert, jab, ncoghlan, pydanny, rhettinger, serhiy.storchaka, tim.peters, ztane
Date 2021-08-04.05:19:26
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1628054366.92.0.193302809619.issue43468@roundup.psfhosted.org>
In-reply-to
Content
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
History
Date User Action Args
2021-08-04 05:19:26rhettingersetrecipients: + rhettinger, tim.peters, ncoghlan, carljm, pydanny, jab, serhiy.storchaka, ztane, graingert
2021-08-04 05:19:26rhettingersetmessageid: <1628054366.92.0.193302809619.issue43468@roundup.psfhosted.org>
2021-08-04 05:19:26rhettingerlinkissue43468 messages
2021-08-04 05:19:26rhettingercreate