Refs #23919 -- Stopped using django.utils.lru_cache().

This commit is contained in:
Aymeric Augustin 2017-01-18 21:30:21 +01:00 committed by Tim Graham
parent 2b281cc35e
commit 3cc5f01d9b
21 changed files with 52 additions and 216 deletions

View File

@ -1,3 +1,4 @@
import functools
import sys
import threading
import warnings
@ -5,7 +6,6 @@ from collections import Counter, OrderedDict, defaultdict
from functools import partial
from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured
from django.utils import lru_cache
from .config import AppConfig
@ -156,7 +156,7 @@ class Apps(object):
raise LookupError(message)
# This method is performance-critical at least for Django's test suite.
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_models(self, include_auto_created=False, include_swapped=False):
"""
Returns a list of all installed models.
@ -268,7 +268,7 @@ class Apps(object):
"Model '%s.%s' not registered." % (app_label, model_name))
return model
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_swappable_settings_name(self, to_string):
"""
For a given model string (e.g. "auth.User"), return the name of the

View File

@ -1,7 +1,8 @@
import functools
from django.conf import settings
from django.conf.urls import url
from django.urls import LocaleRegexURLResolver, get_resolver
from django.utils import lru_cache
from django.views.i18n import set_language
@ -18,7 +19,7 @@ def i18n_patterns(*urls, **kwargs):
return [LocaleRegexURLResolver(list(urls), prefix_default_language=prefix_default_language)]
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def is_language_prefix_patterns_used(urlconf):
"""
Return a tuple of two booleans: (

View File

@ -1,5 +1,6 @@
import base64
import binascii
import functools
import hashlib
import importlib
import warnings
@ -9,7 +10,6 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.signals import setting_changed
from django.dispatch import receiver
from django.utils import lru_cache
from django.utils.crypto import (
constant_time_compare, get_random_string, pbkdf2,
)
@ -82,7 +82,7 @@ def make_password(password, salt=None, hasher='default'):
return hasher.encode(password, salt)
@lru_cache.lru_cache()
@functools.lru_cache()
def get_hashers():
hashers = []
for hasher_path in settings.PASSWORD_HASHERS:
@ -95,7 +95,7 @@ def get_hashers():
return hashers
@lru_cache.lru_cache()
@functools.lru_cache()
def get_hashers_by_algorithm():
return {hasher.algorithm: hasher for hasher in get_hashers()}

View File

@ -1,3 +1,4 @@
import functools
import gzip
import os
import re
@ -7,7 +8,6 @@ from django.conf import settings
from django.core.exceptions import (
FieldDoesNotExist, ImproperlyConfigured, ValidationError,
)
from django.utils import lru_cache
from django.utils._os import upath
from django.utils.encoding import force_text
from django.utils.functional import lazy
@ -16,7 +16,7 @@ from django.utils.module_loading import import_string
from django.utils.translation import ugettext as _, ungettext
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_default_password_validators():
return get_password_validators(settings.AUTH_PASSWORD_VALIDATORS)

View File

@ -1,3 +1,4 @@
import functools
import os
from collections import OrderedDict
@ -8,7 +9,6 @@ from django.core.exceptions import ImproperlyConfigured
from django.core.files.storage import (
FileSystemStorage, Storage, default_storage,
)
from django.utils import lru_cache
from django.utils._os import safe_join
from django.utils.functional import LazyObject, empty
from django.utils.module_loading import import_string
@ -264,7 +264,7 @@ def get_finders():
yield get_finder(finder_path)
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_finder(import_path):
"""
Imports the staticfiles finder class described by import_path, where

View File

@ -1,3 +1,4 @@
import functools
import os
import pkgutil
import sys
@ -12,7 +13,7 @@ from django.core.management.base import (
BaseCommand, CommandError, CommandParser, handle_default_options,
)
from django.core.management.color import color_style
from django.utils import autoreload, lru_cache
from django.utils import autoreload
from django.utils._os import npath, upath
from django.utils.encoding import force_text
@ -39,7 +40,7 @@ def load_command_class(app_name, name):
return module.Command()
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_commands():
"""
Returns a dictionary mapping command names to their callback applications.

View File

@ -2,10 +2,11 @@
Sets up the terminal color scheme.
"""
import functools
import os
import sys
from django.utils import lru_cache, termcolors
from django.utils import termcolors
def supports_color():
@ -57,7 +58,7 @@ def make_style(config_string=''):
return style
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def no_style():
"""
Returns a Style object with no color scheme.

View File

@ -1,3 +1,4 @@
import functools
import glob
import gzip
import os
@ -16,7 +17,6 @@ from django.db import (
DEFAULT_DB_ALIAS, DatabaseError, IntegrityError, connections, router,
transaction,
)
from django.utils import lru_cache
from django.utils._os import upath
from django.utils.encoding import force_text
from django.utils.functional import cached_property
@ -202,7 +202,7 @@ class Command(BaseCommand):
RuntimeWarning
)
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def find_fixtures(self, fixture_label):
"""
Finds fixture files for a given label.

View File

@ -1,3 +1,4 @@
import functools
import inspect
from functools import partial
@ -13,7 +14,6 @@ from django.db.models.query_utils import PathInfo
from django.db.models.utils import make_model_tuple
from django.utils.encoding import force_text
from django.utils.functional import cached_property, curry
from django.utils.lru_cache import lru_cache
from django.utils.translation import ugettext_lazy as _
from . import Field
@ -710,7 +710,7 @@ class ForeignObject(RelatedField):
return pathinfos
@classmethod
@lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_lookups(cls):
bases = inspect.getmro(cls)
bases = bases[:bases.index(ForeignObject) + 1]

View File

@ -5,12 +5,12 @@ Factored out from django.db.models.query to avoid making the main module very
large and/or so that they can be used by other modules without getting into
circular import difficulties.
"""
import functools
import inspect
from collections import namedtuple
from django.db.models.constants import LOOKUP_SEP
from django.utils import tree
from django.utils.lru_cache import lru_cache
# PathInfo is used when converting lookups (fk__somecol). The contents
# describe the relation in Model terms (model Options and Fields for both
@ -137,7 +137,7 @@ class RegisterLookupMixin(object):
return cls.get_lookups().get(lookup_name, None)
@classmethod
@lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_lookups(cls):
class_lookups = [parent.__dict__.get('class_lookups', {}) for parent in inspect.getmro(cls)]
return cls.merge_dicts(class_lookups)

View File

@ -1,9 +1,9 @@
import functools
import os
from django.conf import settings
from django.template.backends.django import DjangoTemplates
from django.template.loader import get_template
from django.utils import lru_cache
from django.utils._os import upath
from django.utils.functional import cached_property
from django.utils.module_loading import import_string
@ -17,7 +17,7 @@ except ImportError:
ROOT = upath(os.path.dirname(__file__))
@lru_cache.lru_cache()
@functools.lru_cache()
def get_default_renderer():
renderer_class = import_string(settings.FORM_RENDERER)
return renderer_class()

View File

@ -1,5 +1,6 @@
import functools
from django.core.exceptions import ImproperlyConfigured
from django.utils import lru_cache
from django.utils.functional import cached_property
from django.utils.module_loading import import_string
@ -52,7 +53,7 @@ class Engine(object):
self.template_builtins = self.get_template_builtins(self.builtins)
@staticmethod
@lru_cache.lru_cache()
@functools.lru_cache()
def get_default():
"""
When only one DjangoTemplates backend is configured, returns it.

View File

@ -1,10 +1,10 @@
import functools
import os
from collections import Counter, OrderedDict
from django.apps import apps
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils import lru_cache
from django.utils._os import upath
from django.utils.functional import cached_property
from django.utils.module_loading import import_string
@ -89,7 +89,7 @@ class EngineHandler(object):
return [self[alias] for alias in self]
@lru_cache.lru_cache()
@functools.lru_cache()
def get_app_template_dirs(dirname):
"""
Return an iterable of paths of directories to load app templates from.

View File

@ -14,7 +14,6 @@ from django.conf import settings
from django.core.checks import Warning
from django.core.checks.urls import check_resolver
from django.core.exceptions import ImproperlyConfigured
from django.utils import lru_cache
from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_str, force_text
from django.utils.functional import cached_property
@ -60,7 +59,7 @@ class ResolverMatch(object):
)
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_resolver(urlconf=None):
if urlconf is None:
from django.conf import settings
@ -68,7 +67,7 @@ def get_resolver(urlconf=None):
return RegexURLResolver(r'^/', urlconf)
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_ns_resolver(ns_pattern, resolver):
# Build a namespaced resolver for the given parent URLconf pattern.
# This makes it possible to have captured parameters in the parent

View File

@ -1,11 +1,11 @@
import functools
from importlib import import_module
from django.core.exceptions import ViewDoesNotExist
from django.utils import lru_cache
from django.utils.module_loading import module_has_submodule
@lru_cache.lru_cache(maxsize=None)
@functools.lru_cache(maxsize=None)
def get_callable(lookup_view):
"""
Return a callable corresponding to lookup_view.

View File

@ -1,172 +1,5 @@
try:
from functools import lru_cache
from functools import lru_cache # noqa
except ImportError:
# backport of Python's 3.3 lru_cache, written by Raymond Hettinger and
# licensed under MIT license, from:
# <http://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/>
# Should be removed when Django only supports Python 3.2 and above.
from collections import namedtuple
from functools import update_wrapper
from threading import RLock
_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])
class _HashedSeq(list):
__slots__ = 'hashvalue'
def __init__(self, tup, hash=hash):
self[:] = tup
self.hashvalue = hash(tup)
def __hash__(self):
return self.hashvalue
def _make_key(args, kwds, typed,
kwd_mark = (object(),),
fasttypes = {int, str, frozenset, type(None)},
sorted=sorted, tuple=tuple, type=type, len=len):
'Make a cache key from optionally typed positional and keyword arguments'
key = args
if kwds:
sorted_items = sorted(kwds.items())
key += kwd_mark
for item in sorted_items:
key += item
if typed:
key += tuple(type(v) for v in args)
if kwds:
key += tuple(type(v) for k, v in sorted_items)
elif len(key) == 1 and type(key[0]) in fasttypes:
return key[0]
return _HashedSeq(key)
def lru_cache(maxsize=100, typed=False):
"""Least-recently-used cache decorator.
If *maxsize* is set to None, the LRU features are disabled and the cache
can grow without bound.
If *typed* is True, arguments of different types will be cached separately.
For example, f(3.0) and f(3) will be treated as distinct calls with
distinct results.
Arguments to the cached function must be hashable.
View the cache statistics named tuple (hits, misses, maxsize, currsize) with
f.cache_info(). Clear the cache and statistics with f.cache_clear().
Access the underlying function with f.__wrapped__.
See: https://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
"""
# Users should only access the lru_cache through its public API:
# cache_info, cache_clear, and f.__wrapped__
# The internals of the lru_cache are encapsulated for thread safety and
# to allow the implementation to change (including a possible C version).
def decorating_function(user_function):
cache = dict()
stats = [0, 0] # make statistics updateable non-locally
HITS, MISSES = 0, 1 # names for the stats fields
make_key = _make_key
cache_get = cache.get # bound method to lookup key or return None
_len = len # localize the global len() function
lock = RLock() # because linkedlist updates aren't threadsafe
root = [] # root of the circular doubly linked list
root[:] = [root, root, None, None] # initialize by pointing to self
nonlocal_root = [root] # make updateable non-locally
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields
if maxsize == 0:
def wrapper(*args, **kwds):
# no caching, just do a statistics update after a successful call
result = user_function(*args, **kwds)
stats[MISSES] += 1
return result
elif maxsize is None:
def wrapper(*args, **kwds):
# simple caching without ordering or size limit
key = make_key(args, kwds, typed)
result = cache_get(key, root) # root used here as a unique not-found sentinel
if result is not root:
stats[HITS] += 1
return result
result = user_function(*args, **kwds)
cache[key] = result
stats[MISSES] += 1
return result
else:
def wrapper(*args, **kwds):
# size limited caching that tracks accesses by recency
key = make_key(args, kwds, typed) if kwds or typed else args
with lock:
link = cache_get(key)
if link is not None:
# record recent use of the key by moving it to the front of the list
root, = nonlocal_root
link_prev, link_next, key, result = link
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
last = root[PREV]
last[NEXT] = root[PREV] = link
link[PREV] = last
link[NEXT] = root
stats[HITS] += 1
return result
result = user_function(*args, **kwds)
with lock:
root, = nonlocal_root
if key in cache:
# getting here means that this same key was added to the
# cache while the lock was released. since the link
# update is already done, we need only return the
# computed result and update the count of misses.
pass
elif _len(cache) >= maxsize:
# use the old root to store the new key and result
oldroot = root
oldroot[KEY] = key
oldroot[RESULT] = result
# empty the oldest link and make it the new root
root = nonlocal_root[0] = oldroot[NEXT]
oldkey = root[KEY]
oldvalue = root[RESULT]
root[KEY] = root[RESULT] = None
# now update the cache dictionary for the new links
del cache[oldkey]
cache[key] = oldroot
else:
# put result in a new link at the front of the list
last = root[PREV]
link = [last, root, key, result]
last[NEXT] = root[PREV] = cache[key] = link
stats[MISSES] += 1
return result
def cache_info():
"""Report cache statistics"""
with lock:
return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache))
def cache_clear():
"""Clear the cache and cache statistics"""
with lock:
cache.clear()
root = nonlocal_root[0]
root[:] = [root, root, None, None]
stats[:] = [0, 0]
wrapper.__wrapped__ = user_function
wrapper.cache_info = cache_info
wrapper.cache_clear = cache_clear
return update_wrapper(wrapper, user_function)
return decorating_function
# Deprecate or remove this module when no supported version of Django still
# supports Python 2. Until then, keep it to allow pluggable apps to support
# Python 2 and Python 3 without raising a deprecation warning.

View File

@ -2,13 +2,13 @@
Timezone-related classes and functions.
"""
import functools
from datetime import datetime, timedelta, tzinfo
from threading import local
import pytz
from django.conf import settings
from django.utils import lru_cache
from django.utils.decorators import ContextDecorator
__all__ = [
@ -69,7 +69,7 @@ def get_fixed_timezone(offset):
# In order to avoid accessing settings at compile time,
# wrap the logic in a function and cache the result.
@lru_cache.lru_cache()
@functools.lru_cache()
def get_default_timezone():
"""
Returns the default time zone as a tzinfo instance.

View File

@ -1,4 +1,5 @@
"""Translation helper functions."""
import functools
import gettext as gettext_module
import os
import re
@ -13,7 +14,6 @@ from django.conf.locale import LANG_INFO
from django.core.exceptions import AppRegistryNotReady
from django.core.signals import setting_changed
from django.dispatch import receiver
from django.utils import lru_cache
from django.utils._os import upath
from django.utils.encoding import force_text
from django.utils.safestring import SafeData, mark_safe
@ -403,7 +403,7 @@ def all_locale_paths():
return [globalpath] + list(settings.LOCALE_PATHS)
@lru_cache.lru_cache(maxsize=1000)
@functools.lru_cache(maxsize=1000)
def check_for_language(lang_code):
"""
Checks whether there is a global language file for the given language
@ -423,7 +423,7 @@ def check_for_language(lang_code):
return False
@lru_cache.lru_cache()
@functools.lru_cache()
def get_languages():
"""
Cache of settings.LANGUAGES in an OrderedDict for easy lookups by key.
@ -431,7 +431,7 @@ def get_languages():
return OrderedDict(settings.LANGUAGES)
@lru_cache.lru_cache(maxsize=1000)
@functools.lru_cache(maxsize=1000)
def get_supported_language_variant(lang_code, strict=False):
"""
Returns the language-code that's listed in supported languages, possibly

View File

@ -1,9 +1,8 @@
import datetime
import functools
import os
import subprocess
from django.utils.lru_cache import lru_cache
def get_version(version=None):
"Returns a PEP 440-compliant version number from VERSION."
@ -57,7 +56,7 @@ def get_docs_version(version=None):
return '%d.%d' % version[:2]
@lru_cache()
@functools.lru_cache()
def get_git_changeset():
"""Returns a numeric identifier of the latest git changeset.

View File

@ -1,3 +1,4 @@
import functools
import re
import sys
import types
@ -7,7 +8,7 @@ from django.http import HttpResponse, HttpResponseNotFound
from django.template import Context, Engine, TemplateDoesNotExist
from django.template.defaultfilters import force_escape, pprint
from django.urls import Resolver404, resolve
from django.utils import lru_cache, timezone
from django.utils import timezone
from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_bytes, force_text
from django.utils.module_loading import import_string
@ -83,7 +84,7 @@ def technical_500_response(request, exc_type, exc_value, tb, status_code=500):
return HttpResponse(html, status=status_code, content_type='text/html')
@lru_cache.lru_cache()
@functools.lru_cache()
def get_default_exception_reporter_filter():
# Instantiate the default filter for the first time and cache it.
return import_string(settings.DEFAULT_EXCEPTION_REPORTER_FILTER)()

View File

@ -3,7 +3,7 @@ doc_files = docs extras AUTHORS INSTALL LICENSE README.rst
install-script = scripts/rpm-install.sh
[flake8]
exclude = build,.git,.tox,./django/utils/lru_cache.py,./django/utils/six.py,./django/conf/app_template/*,./tests/.env,./xmlrunner
exclude = build,.git,.tox,./django/utils/six.py,./django/conf/app_template/*,./tests/.env,./xmlrunner
ignore = W601
max-line-length = 119