diff --git a/synapse/util/caches/deferred_cache.py b/synapse/util/caches/deferred_cache.py
index 377c9a282a..3c4cc093af 100644
--- a/synapse/util/caches/deferred_cache.py
+++ b/synapse/util/caches/deferred_cache.py
@@ -22,7 +22,6 @@ from typing import (
Iterable,
MutableMapping,
Optional,
- Sized,
TypeVar,
Union,
cast,
@@ -105,13 +104,7 @@ class DeferredCache(Generic[KT, VT]):
max_size=max_entries,
cache_name=name,
cache_type=cache_type,
- size_callback=(
- (lambda d: len(cast(Sized, d)) or 1)
- # Argument 1 to "len" has incompatible type "VT"; expected "Sized"
- # We trust that `VT` is `Sized` when `iterable` is `True`
- if iterable
- else None
- ),
+ size_callback=(lambda d: len(d) or 1) if iterable else None,
metrics_collection_callback=metrics_cb,
apply_cache_factor_from_config=apply_cache_factor_from_config,
prune_unread_entries=prune_unread_entries,
diff --git a/synapse/util/caches/lrucache.py b/synapse/util/caches/lrucache.py
index eb96f7e665..a0a7a9de32 100644
--- a/synapse/util/caches/lrucache.py
+++ b/synapse/util/caches/lrucache.py
@@ -15,15 +15,14 @@
import logging
import threading
import weakref
-from enum import Enum
from functools import wraps
from typing import (
TYPE_CHECKING,
Any,
Callable,
Collection,
- Dict,
Generic,
+ Iterable,
List,
Optional,
Type,
@@ -191,7 +190,7 @@ class _Node(Generic[KT, VT]):
root: "ListNode[_Node]",
key: KT,
value: VT,
- cache: "weakref.ReferenceType[LruCache[KT, VT]]",
+ cache: "weakref.ReferenceType[LruCache]",
clock: Clock,
callbacks: Collection[Callable[[], None]] = (),
prune_unread_entries: bool = True,
@@ -271,10 +270,7 @@ class _Node(Generic[KT, VT]):
removed from all lists.
"""
cache = self._cache()
- if (
- cache is None
- or cache.pop(self.key, _Sentinel.sentinel) is _Sentinel.sentinel
- ):
+ if not cache or not cache.pop(self.key, None):
# `cache.pop` should call `drop_from_lists()`, unless this Node had
# already been removed from the cache.
self.drop_from_lists()
@@ -294,12 +290,6 @@ class _Node(Generic[KT, VT]):
self._global_list_node.update_last_access(clock)
-class _Sentinel(Enum):
- # defining a sentinel in this way allows mypy to correctly handle the
- # type of a dictionary lookup.
- sentinel = object()
-
-
class LruCache(Generic[KT, VT]):
"""
Least-recently-used cache, supporting prometheus metrics and invalidation callbacks.
@@ -312,7 +302,7 @@ class LruCache(Generic[KT, VT]):
max_size: int,
cache_name: Optional[str] = None,
cache_type: Type[Union[dict, TreeCache]] = dict,
- size_callback: Optional[Callable[[VT], int]] = None,
+ size_callback: Optional[Callable] = None,
metrics_collection_callback: Optional[Callable[[], None]] = None,
apply_cache_factor_from_config: bool = True,
clock: Optional[Clock] = None,
@@ -349,7 +339,7 @@ class LruCache(Generic[KT, VT]):
else:
real_clock = clock
- cache: Union[Dict[KT, _Node[KT, VT]], TreeCache] = cache_type()
+ cache = cache_type()
self.cache = cache # Used for introspection.
self.apply_cache_factor_from_config = apply_cache_factor_from_config
@@ -384,7 +374,7 @@ class LruCache(Generic[KT, VT]):
# creating more each time we create a `_Node`.
weak_ref_to_self = weakref.ref(self)
- list_root = ListNode[_Node[KT, VT]].create_root_node()
+ list_root = ListNode[_Node].create_root_node()
lock = threading.Lock()
@@ -432,7 +422,7 @@ class LruCache(Generic[KT, VT]):
def add_node(
key: KT, value: VT, callbacks: Collection[Callable[[], None]] = ()
) -> None:
- node: _Node[KT, VT] = _Node(
+ node = _Node(
list_root,
key,
value,
@@ -449,10 +439,10 @@ class LruCache(Generic[KT, VT]):
if caches.TRACK_MEMORY_USAGE and metrics:
metrics.inc_memory_usage(node.memory)
- def move_node_to_front(node: _Node[KT, VT]) -> None:
+ def move_node_to_front(node: _Node) -> None:
node.move_to_front(real_clock, list_root)
- def delete_node(node: _Node[KT, VT]) -> int:
+ def delete_node(node: _Node) -> int:
node.drop_from_lists()
deleted_len = 1
@@ -506,7 +496,7 @@ class LruCache(Generic[KT, VT]):
@synchronized
def cache_set(
- key: KT, value: VT, callbacks: Collection[Callable[[], None]] = ()
+ key: KT, value: VT, callbacks: Iterable[Callable[[], None]] = ()
) -> None:
node = cache.get(key, None)
if node is not None:
@@ -600,6 +590,8 @@ class LruCache(Generic[KT, VT]):
def cache_contains(key: KT) -> bool:
return key in cache
+ self.sentinel = object()
+
# make sure that we clear out any excess entries after we get resized.
self._on_resize = evict
@@ -616,18 +608,18 @@ class LruCache(Generic[KT, VT]):
self.clear = cache_clear
def __getitem__(self, key: KT) -> VT:
- result = self.get(key, _Sentinel.sentinel)
- if result is _Sentinel.sentinel:
+ result = self.get(key, self.sentinel)
+ if result is self.sentinel:
raise KeyError()
else:
- return result
+ return cast(VT, result)
def __setitem__(self, key: KT, value: VT) -> None:
self.set(key, value)
def __delitem__(self, key: KT, value: VT) -> None:
- result = self.pop(key, _Sentinel.sentinel)
- if result is _Sentinel.sentinel:
+ result = self.pop(key, self.sentinel)
+ if result is self.sentinel:
raise KeyError()
def __len__(self) -> int:
diff --git a/synapse/util/linked_list.py b/synapse/util/linked_list.py
index 8efbf061aa..9f4be757ba 100644
--- a/synapse/util/linked_list.py
+++ b/synapse/util/linked_list.py
@@ -84,7 +84,7 @@ class ListNode(Generic[P]):
# immediately rather than at the next GC.
self.cache_entry = None
- def move_after(self, node: "ListNode[P]") -> None:
+ def move_after(self, node: "ListNode") -> None:
"""Move this node from its current location in the list to after the
given node.
"""
@@ -122,7 +122,7 @@ class ListNode(Generic[P]):
self.prev_node = None
self.next_node = None
- def _refs_insert_after(self, node: "ListNode[P]") -> None:
+ def _refs_insert_after(self, node: "ListNode") -> None:
"""Internal method to insert the node after the given node."""
# This method should only be called when we're not already in the list.
diff --git a/synapse/util/versionstring.py b/synapse/util/versionstring.py
index c144ff62c1..899ee0adc8 100644
--- a/synapse/util/versionstring.py
+++ b/synapse/util/versionstring.py
@@ -1,5 +1,4 @@
# Copyright 2016 OpenMarket Ltd
-# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -30,11 +29,10 @@ def get_version_string(module: ModuleType) -> str:
If called on a module not in a git checkout will return `__version__`.
Args:
- module: The module to check the version of. Must declare a __version__
- attribute.
+ module (module)
Returns:
- The module version (as a string).
+ str
"""
cached_version = version_cache.get(module)
@@ -46,37 +44,71 @@ def get_version_string(module: ModuleType) -> str:
version_string = module.__version__ # type: ignore[attr-defined]
try:
+ null = open(os.devnull, "w")
cwd = os.path.dirname(os.path.abspath(module.__file__))
- def _run_git_command(prefix: str, *params: str) -> str:
- try:
- result = (
- subprocess.check_output(
- ["git", *params], stderr=subprocess.DEVNULL, cwd=cwd
- )
- .strip()
- .decode("ascii")
+ try:
+ git_branch = (
+ subprocess.check_output(
+ ["git", "rev-parse", "--abbrev-ref", "HEAD"], stderr=null, cwd=cwd
)
- return prefix + result
- except (subprocess.CalledProcessError, FileNotFoundError):
- return ""
-
- git_branch = _run_git_command("b=", "rev-parse", "--abbrev-ref", "HEAD")
- git_tag = _run_git_command("t=", "describe", "--exact-match")
- git_commit = _run_git_command("", "rev-parse", "--short", "HEAD")
+ .strip()
+ .decode("ascii")
+ )
+ git_branch = "b=" + git_branch
+ except (subprocess.CalledProcessError, FileNotFoundError):
+ # FileNotFoundError can arise when git is not installed
+ git_branch = ""
+
+ try:
+ git_tag = (
+ subprocess.check_output(
+ ["git", "describe", "--exact-match"], stderr=null, cwd=cwd
+ )
+ .strip()
+ .decode("ascii")
+ )
+ git_tag = "t=" + git_tag
+ except (subprocess.CalledProcessError, FileNotFoundError):
+ git_tag = ""
+
+ try:
+ git_commit = (
+ subprocess.check_output(
+ ["git", "rev-parse", "--short", "HEAD"], stderr=null, cwd=cwd
+ )
+ .strip()
+ .decode("ascii")
+ )
+ except (subprocess.CalledProcessError, FileNotFoundError):
+ git_commit = ""
+
+ try:
+ dirty_string = "-this_is_a_dirty_checkout"
+ is_dirty = (
+ subprocess.check_output(
+ ["git", "describe", "--dirty=" + dirty_string], stderr=null, cwd=cwd
+ )
+ .strip()
+ .decode("ascii")
+ .endswith(dirty_string)
+ )
- dirty_string = "-this_is_a_dirty_checkout"
- is_dirty = _run_git_command("", "describe", "--dirty=" + dirty_string).endswith(
- dirty_string
- )
- git_dirty = "dirty" if is_dirty else ""
+ git_dirty = "dirty" if is_dirty else ""
+ except (subprocess.CalledProcessError, FileNotFoundError):
+ git_dirty = ""
if git_branch or git_tag or git_commit or git_dirty:
git_version = ",".join(
s for s in (git_branch, git_tag, git_commit, git_dirty) if s
)
- version_string = f"{version_string} ({git_version})"
+ version_string = "%s (%s)" % (
+ # If the __version__ attribute doesn't exist, we'll have failed
+ # loudly above.
+ module.__version__, # type: ignore[attr-defined]
+ git_version,
+ )
except Exception as e:
logger.info("Failed to check for git repository: %s", e)
|