Browse Source

[fix] add module for backward compatibility

cache_property has been added in py3.8 [1]

To support cache_property in py3.7 the implementation from 3.8 has been
copied to compat.py.  This code can be cleanup with EOL of py3.7.

[1] https://docs.python.org/3/library/functools.html#functools.cached_property

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
Markus Heiser 3 years ago
parent
commit
59100e8525
2 changed files with 71 additions and 1 deletions
  1. 70 0
      searx/compat.py
  2. 1 1
      searx/infopage/__init__.py

+ 70 - 0
searx/compat.py

@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# lint: pylint
+# pyright: basic
+"""Module for backward compatibility.
+
+"""
+# pylint: disable=C,R
+
+
+try:
+    from functools import cached_property  # pylint: disable=unused-import
+
+except ImportError:
+
+    # cache_property has been added in py3.8 [1]
+    #
+    # To support cache_property in py3.7 the implementation from 3.8 has been
+    # copied here.  This code can be cleanup with EOL of py3.7.
+    #
+    # [1] https://docs.python.org/3/library/functools.html#functools.cached_property
+
+    from threading import RLock
+
+    _NOT_FOUND = object()
+
+    class cached_property:
+        def __init__(self, func):
+            self.func = func
+            self.attrname = None
+            self.__doc__ = func.__doc__
+            self.lock = RLock()
+
+        def __set_name__(self, owner, name):
+            if self.attrname is None:
+                self.attrname = name
+            elif name != self.attrname:
+                raise TypeError(
+                    "Cannot assign the same cached_property to two different names "
+                    f"({self.attrname!r} and {name!r})."
+                )
+
+        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
+            val = cache.get(self.attrname, _NOT_FOUND)
+            if val is _NOT_FOUND:
+                with self.lock:
+                    # check if another thread filled cache while we awaited lock
+                    val = cache.get(self.attrname, _NOT_FOUND)
+                    if val is _NOT_FOUND:
+                        val = self.func(instance)
+                        try:
+                            cache[self.attrname] = val
+                        except TypeError:
+                            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
+            return val

+ 1 - 1
searx/infopage/__init__.py

@@ -23,7 +23,6 @@ __all__ = ['InfoPage', 'MistletoePage', 'InfoPageSet']
 
 import os.path
 import logging
-from functools import cached_property
 import typing
 
 import urllib.parse
@@ -32,6 +31,7 @@ from flask.helpers import url_for
 import mistletoe
 
 from .. import get_setting
+from ..compat import cached_property
 from ..version import GIT_URL
 
 logger = logging.getLogger('doc')