[tor-commits] [stem/master] Drop lru_cache copy

atagar at torproject.org atagar at torproject.org
Sun Jan 5 21:39:28 UTC 2020


commit 2e0fb02f938c6c2da3b31780753df908655a6ba8
Author: Damian Johnson <atagar at torproject.org>
Date:   Fri Jan 3 13:56:14 2020 -0800

    Drop lru_cache copy
    
    Python added lru_cache as a builtin in Python 3.2. We included a copy for
    Python 2.x compatibity and as such is no longer required.
---
 stem/descriptor/extrainfo_descriptor.py |   7 +-
 stem/descriptor/hidden_service.py       |   8 +-
 stem/descriptor/microdescriptor.py      |   8 +-
 stem/descriptor/server_descriptor.py    |  13 +--
 stem/exit_policy.py                     |  20 ++--
 stem/interpreter/autocomplete.py        |   9 +-
 stem/interpreter/help.py                |   9 +-
 stem/manual.py                          |   8 +-
 stem/prereq.py                          |  15 ---
 stem/util/lru_cache.py                  | 182 --------------------------------
 stem/util/proc.py                       |  12 +--
 stem/version.py                         |   8 +-
 12 files changed, 31 insertions(+), 268 deletions(-)

diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index 17082f88..f53d9502 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -100,11 +100,6 @@ from stem.descriptor import (
   _random_crypto_blob,
 )
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 # known statuses for dirreq-v2-resp and dirreq-v3-resp...
 DirResponse = stem.util.enum.Enum(
   ('OK', 'ok'),
@@ -950,7 +945,7 @@ class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
   def create(cls, attr = None, exclude = (), validate = True, sign = False, signing_key = None):
     return cls(cls.content(attr, exclude, sign, signing_key), validate = validate)
 
-  @lru_cache()
+  @functools.lru_cache()
   def digest(self, hash_type = DigestHash.SHA1, encoding = DigestEncoding.HEX):
     if hash_type == DigestHash.SHA1:
       # our digest is calculated from everything except our signature
diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py
index e5f82861..7994bffb 100644
--- a/stem/descriptor/hidden_service.py
+++ b/stem/descriptor/hidden_service.py
@@ -35,6 +35,7 @@ import base64
 import binascii
 import collections
 import datetime
+import functools
 import hashlib
 import io
 import os
@@ -70,11 +71,6 @@ from stem.descriptor import (
   _random_crypto_blob,
 )
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 try:
   from cryptography.hazmat.backends.openssl.backend import backend
   X25519_AVAILABLE = hasattr(backend, 'x25519_supported') and backend.x25519_supported()
@@ -745,7 +741,7 @@ class HiddenServiceDescriptorV2(BaseHiddenServiceDescriptor):
     else:
       self._entries = entries
 
-  @lru_cache()
+  @functools.lru_cache()
   def introduction_points(self, authentication_cookie = None):
     """
     Provided this service's introduction points.
diff --git a/stem/descriptor/microdescriptor.py b/stem/descriptor/microdescriptor.py
index 81bcb43c..17d06d90 100644
--- a/stem/descriptor/microdescriptor.py
+++ b/stem/descriptor/microdescriptor.py
@@ -64,6 +64,7 @@ Doing the same is trivial with server descriptors...
   Microdescriptor - Tor microdescriptor.
 """
 
+import functools
 import hashlib
 
 import stem.exit_policy
@@ -88,11 +89,6 @@ from stem.descriptor.router_status_entry import (
   _parse_p_line,
 )
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 REQUIRED_FIELDS = (
   'onion-key',
 )
@@ -305,7 +301,7 @@ class Microdescriptor(Descriptor):
     else:
       raise NotImplementedError('Microdescriptor digests are only available in sha1 and sha256, not %s' % hash_type)
 
-  @lru_cache()
+  @functools.lru_cache()
   def get_annotations(self):
     """
     Provides content that appeared prior to the descriptor. If this comes from
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 1d9adf32..84ba8b65 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -92,11 +92,6 @@ from stem.descriptor import (
   _random_crypto_blob,
 )
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 # relay descriptors must have exactly one of the following
 REQUIRED_FIELDS = (
   'router',
@@ -668,7 +663,7 @@ class ServerDescriptor(Descriptor):
 
     raise NotImplementedError('Unsupported Operation: this should be implemented by the ServerDescriptor subclass')
 
-  @lru_cache()
+  @functools.lru_cache()
   def get_annotations(self):
     """
     Provides content that appeared prior to the descriptor. If this comes from
@@ -910,7 +905,7 @@ class RelayDescriptor(ServerDescriptor):
   def create(cls, attr = None, exclude = (), validate = True, sign = False, signing_key = None, exit_policy = None):
     return cls(cls.content(attr, exclude, sign, signing_key, exit_policy), validate = validate, skip_crypto_validation = not sign)
 
-  @lru_cache()
+  @functools.lru_cache()
   def digest(self, hash_type = DigestHash.SHA1, encoding = DigestEncoding.HEX):
     """
     Provides the digest of our descriptor's content.
@@ -967,7 +962,7 @@ class RelayDescriptor(ServerDescriptor):
 
     return RouterStatusEntryV3.create(attr)
 
-  @lru_cache()
+  @functools.lru_cache()
   def _onion_key_crosscert_digest(self):
     """
     Provides the digest of the onion-key-crosscert data. This consists of the
@@ -1051,7 +1046,7 @@ class BridgeDescriptor(ServerDescriptor):
 
     return self.get_scrubbing_issues() == []
 
-  @lru_cache()
+  @functools.lru_cache()
   def get_scrubbing_issues(self):
     """
     Provides issues with our scrubbing.
diff --git a/stem/exit_policy.py b/stem/exit_policy.py
index ddaf719c..f9d7e6e0 100644
--- a/stem/exit_policy.py
+++ b/stem/exit_policy.py
@@ -67,6 +67,7 @@ exiting to a destination is permissible or not. For instance...
 
 from __future__ import absolute_import
 
+import functools
 import re
 import socket
 import zlib
@@ -77,11 +78,6 @@ import stem.util.connection
 import stem.util.enum
 import stem.util.str_tools
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 AddressType = stem.util.enum.Enum(('WILDCARD', 'Wildcard'), ('IPv4', 'IPv4'), ('IPv6', 'IPv6'))
 
 # Addresses aliased by the 'private' policy. From the tor man page...
@@ -271,7 +267,7 @@ class ExitPolicy(object):
 
     self._is_allowed_default = True
 
-  @lru_cache()
+  @functools.lru_cache()
   def can_exit_to(self, address = None, port = None, strict = False):
     """
     Checks if this policy allows exiting to a given destination or not. If the
@@ -295,7 +291,7 @@ class ExitPolicy(object):
 
     return self._is_allowed_default
 
-  @lru_cache()
+  @functools.lru_cache()
   def is_exiting_allowed(self):
     """
     Provides **True** if the policy allows exiting whatsoever, **False**
@@ -317,7 +313,7 @@ class ExitPolicy(object):
 
     return self._is_allowed_default
 
-  @lru_cache()
+  @functools.lru_cache()
   def summary(self):
     """
     Provides a short description of our policy chain, similar to a
@@ -520,7 +516,7 @@ class ExitPolicy(object):
     for rule in self._get_rules():
       yield rule
 
-  @lru_cache()
+  @functools.lru_cache()
   def __str__(self):
     return ', '.join([str(rule) for rule in self._get_rules()])
 
@@ -873,7 +869,7 @@ class ExitPolicyRule(object):
 
     return self._is_default_suffix
 
-  @lru_cache()
+  @functools.lru_cache()
   def __str__(self):
     """
     Provides the string representation of our policy. This does not
@@ -917,13 +913,13 @@ class ExitPolicyRule(object):
 
     return label
 
-  @lru_cache()
+  @functools.lru_cache()
   def _get_mask_bin(self):
     # provides an integer representation of our mask
 
     return int(stem.util.connection._address_to_binary(self.get_mask(False)), 2)
 
-  @lru_cache()
+  @functools.lru_cache()
   def _get_address_bin(self):
     # provides an integer representation of our address
 
diff --git a/stem/interpreter/autocomplete.py b/stem/interpreter/autocomplete.py
index b6c5354c..05585b48 100644
--- a/stem/interpreter/autocomplete.py
+++ b/stem/interpreter/autocomplete.py
@@ -5,15 +5,12 @@
 Tab completion for our interpreter prompt.
 """
 
+import functools
+
 import stem.prereq
 
 from stem.interpreter import uses_settings
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 
 @uses_settings
 def _get_commands(controller, config):
@@ -84,7 +81,7 @@ class Autocompleter(object):
   def __init__(self, controller):
     self._commands = _get_commands(controller)
 
-  @lru_cache()
+  @functools.lru_cache()
   def matches(self, text):
     """
     Provides autocompletion matches for the given text.
diff --git a/stem/interpreter/help.py b/stem/interpreter/help.py
index d2e08d5c..5fde9246 100644
--- a/stem/interpreter/help.py
+++ b/stem/interpreter/help.py
@@ -5,6 +5,8 @@
 Provides our /help responses.
 """
 
+import functools
+
 import stem.prereq
 
 from stem.interpreter import (
@@ -17,11 +19,6 @@ from stem.interpreter import (
 
 from stem.util.term import format
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 
 def response(controller, arg):
   """
@@ -55,7 +52,7 @@ def _normalize(arg):
   return arg
 
 
- at lru_cache()
+ at functools.lru_cache()
 @uses_settings
 def _response(controller, arg, config):
   if not arg:
diff --git a/stem/manual.py b/stem/manual.py
index b94d4e0b..2176067c 100644
--- a/stem/manual.py
+++ b/stem/manual.py
@@ -49,6 +49,7 @@ us what our torrc options do...
 """
 
 import collections
+import functools
 import os
 import shutil
 import sys
@@ -62,11 +63,6 @@ import stem.util.enum
 import stem.util.log
 import stem.util.system
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 try:
   # account for urllib's change between python 2.x and 3.x
   import urllib.request as urllib
@@ -194,7 +190,7 @@ class ConfigOption(object):
     return not self == other
 
 
- at lru_cache()
+ at functools.lru_cache()
 def _config(lowercase = True):
   """
   Provides a dictionary for our settings.cfg. This has a couple categories...
diff --git a/stem/prereq.py b/stem/prereq.py
index 480f070c..0de2191e 100644
--- a/stem/prereq.py
+++ b/stem/prereq.py
@@ -249,21 +249,6 @@ def is_mock_available():
     return False
 
 
-def _is_lru_cache_available():
-  """
-  Functools added lru_cache to the standard library in Python 3.2. Prior to
-  this using a bundled implementation. We're also using this with Python 3.5
-  due to a buggy implementation. (:trac:`26412`)
-  """
-
-  major_version, minor_version = sys.version_info[0:2]
-
-  if major_version == 3 and minor_version == 5:
-    return False
-  else:
-    return hasattr(functools, 'lru_cache')
-
-
 def _is_sha3_available():
   """
   Check if hashlib has sha3 support. This requires Python 3.6+ *or* the `pysha3
diff --git a/stem/util/lru_cache.py b/stem/util/lru_cache.py
deleted file mode 100644
index 011d4456..00000000
--- a/stem/util/lru_cache.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# Drop in replace for python 3.2's collections.lru_cache, from...
-# http://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/
-#
-# ... which is under the MIT license. Stem users should *not* rely upon this
-# module. It will be removed when we drop support for python 3.2 and below.
-
-"""
-Memoization decorator that caches a function's return value. If later called
-with the same arguments then the cached value is returned rather than
-reevaluated.
-
-This is a a python 2.x port of `functools.lru_cache
-<http://docs.python.org/3/library/functools.html#functools.lru_cache>`_. If
-using python 3.2 or later you should use that instead.
-"""
-
-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 = set([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:  http://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]
-                        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
diff --git a/stem/util/proc.py b/stem/util/proc.py
index f0e0104f..ecb7f3f7 100644
--- a/stem/util/proc.py
+++ b/stem/util/proc.py
@@ -48,6 +48,7 @@ future, use them at your own risk.**
 """
 
 import base64
+import functools
 import os
 import platform
 import socket
@@ -68,11 +69,6 @@ try:
 except ImportError:
   IS_PWD_AVAILABLE = False
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 # os.sysconf is only defined on unix
 try:
   CLOCK_TICKS = os.sysconf(os.sysconf_names['SC_CLK_TCK'])
@@ -88,7 +84,7 @@ Stat = stem.util.enum.Enum(
 )
 
 
- at lru_cache()
+ at functools.lru_cache()
 def is_available():
   """
   Checks if proc information is available on this platform.
@@ -109,7 +105,7 @@ def is_available():
     return True
 
 
- at lru_cache()
+ at functools.lru_cache()
 def system_start_time():
   """
   Provides the unix time (seconds since epoch) when the system started.
@@ -132,7 +128,7 @@ def system_start_time():
     raise exc
 
 
- at lru_cache()
+ at functools.lru_cache()
 def physical_memory():
   """
   Provides the total physical memory on the system in bytes.
diff --git a/stem/version.py b/stem/version.py
index 6bf0befe..71f16e2c 100644
--- a/stem/version.py
+++ b/stem/version.py
@@ -84,6 +84,7 @@ easily parsed and compared, for instance...
   ===================================== ===========
 """
 
+import functools
 import os
 import re
 
@@ -92,11 +93,6 @@ import stem.util
 import stem.util.enum
 import stem.util.system
 
-if stem.prereq._is_lru_cache_available():
-  from functools import lru_cache
-else:
-  from stem.util.lru_cache import lru_cache
-
 # cache for the get_system_tor_version function
 VERSION_CACHE = {}
 
@@ -150,7 +146,7 @@ def get_system_tor_version(tor_cmd = 'tor'):
   return VERSION_CACHE[tor_cmd]
 
 
- at lru_cache()
+ at functools.lru_cache()
 def _get_version(version_str):
   return Version(version_str)
 





More information about the tor-commits mailing list