1
0
mirror of https://github.com/django/django.git synced 2025-10-24 22:26:08 +00:00

Fixed #29478 -- Added support for mangled names to cached_property.

Co-Authored-By: Sergey Fedoseev <fedoseev.sergey@gmail.com>
This commit is contained in:
Thomas Grainger
2018-06-07 14:03:45 +01:00
committed by Tim Graham
parent 80ba7a881f
commit 0607699902
4 changed files with 239 additions and 31 deletions

View File

@@ -3,6 +3,8 @@ import itertools
import operator
from functools import total_ordering, wraps
from django.utils.version import PY36, get_docs_version
# You can't trivially replace this with `functools.partial` because this binds
# to classes and returns bound instances, whereas functools.partial (on
@@ -18,13 +20,54 @@ class cached_property:
Decorator that converts a method with a single self argument into a
property cached on the instance.
Optional ``name`` argument allows you to make cached properties of other
methods. (e.g. url = cached_property(get_absolute_url, name='url') )
A cached property can be made out of an existing method:
(e.g. ``url = cached_property(get_absolute_url)``).
On Python < 3.6, the optional ``name`` argument must be provided, e.g.
``url = cached_property(get_absolute_url, name='url')``.
"""
name = None
@staticmethod
def func(instance):
raise TypeError(
'Cannot use cached_property instance without calling '
'__set_name__() on it.'
)
@staticmethod
def _is_mangled(name):
return name.startswith('__') and not name.endswith('__')
def __init__(self, func, name=None):
self.func = func
if PY36:
self.real_func = func
else:
func_name = func.__name__
name = name or func_name
if not (isinstance(name, str) and name.isidentifier()):
raise ValueError(
"%r can't be used as the name of a cached_property." % name,
)
if self._is_mangled(name):
raise ValueError(
'cached_property does not work with mangled methods on '
'Python < 3.6 without the appropriate `name` argument. See '
'https://docs.djangoproject.com/en/%s/ref/utils/'
'#cached-property-mangled-name' % get_docs_version(),
)
self.name = name
self.func = func
self.__doc__ = getattr(func, '__doc__')
self.name = name or func.__name__
def __set_name__(self, owner, name):
if self.name is None:
self.name = name
self.func = self.real_func
elif name != self.name:
raise TypeError(
"Cannot assign the same cached_property to two different names "
"(%r and %r)." % (self.name, name)
)
def __get__(self, instance, cls=None):
"""