Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F9336843
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Subscribers
None
View Options
diff --git a/swh/counters/__init__.py b/swh/counters/__init__.py
index bcc234d..d762c6f 100644
--- a/swh/counters/__init__.py
+++ b/swh/counters/__init__.py
@@ -1,75 +1,76 @@
# Copyright (C) 2021 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
from __future__ import annotations
import importlib
from typing import TYPE_CHECKING, Any, Dict
if TYPE_CHECKING:
from swh.counters.interface import CountersInterface, HistoryInterface
COUNTERS_IMPLEMENTATIONS = {
"redis": ".redis.Redis",
"remote": ".api.client.RemoteCounters",
+ "memory": ".in_memory.InMemory",
}
HISTORY_IMPLEMENTATIONS = {
"prometheus": ".history.History",
}
def get_counters(cls: str, **kwargs: Dict[str, Any]) -> CountersInterface:
"""Get an counters object of class `cls` with arguments `args`.
Args:
cls: counters's class, either 'local' or 'remote'
args: dictionary of arguments passed to the
counters class constructor
Returns:
an instance of swh.counters's classes (either local or remote)
Raises:
ValueError if passed an unknown counters class.
"""
class_path = COUNTERS_IMPLEMENTATIONS.get(cls)
if class_path is None:
raise ValueError(
"Unknown counters class `%s`. Supported: %s"
% (cls, ", ".join(COUNTERS_IMPLEMENTATIONS))
)
(module_path, class_name) = class_path.rsplit(".", 1)
module = importlib.import_module(module_path, package=__package__)
Counters = getattr(module, class_name)
return Counters(**kwargs)
def get_history(cls: str, **kwargs: Dict[str, Any]) -> HistoryInterface:
"""Get a history object of class `cls` with arguments `kwargs`.
Args:
cls: history's class, only 'prometheus' is supported actually
kwargs: dictionary of arguments passed to the
counters class constructor
Returns:
an instance of swh.counters.history's classes (either local or remote)
Raises:
ValueError if passed an unknown history class.
"""
class_path = HISTORY_IMPLEMENTATIONS.get(cls)
if class_path is None:
raise ValueError(
"Unknown history class `%s`. Supported: %s"
% (cls, ", ".join(HISTORY_IMPLEMENTATIONS))
)
(module_path, class_name) = class_path.rsplit(".", 1)
module = importlib.import_module(module_path, package=__package__)
History = getattr(module, class_name)
return History(**kwargs)
diff --git a/swh/counters/in_memory.py b/swh/counters/in_memory.py
new file mode 100644
index 0000000..d4c4889
--- /dev/null
+++ b/swh/counters/in_memory.py
@@ -0,0 +1,31 @@
+# Copyright (C) 2021 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from collections import defaultdict
+from typing import Any, Dict, Iterable, List
+
+
+class InMemory:
+ """InMemory implementation of the counters.
+ Naive implementation using a Dict[str, Set]"""
+
+ def __init__(self):
+ self.counters = defaultdict(set)
+
+ def check(self):
+ return "OK"
+
+ def add(self, collection: str, keys: Iterable[Any]) -> None:
+ for value in keys:
+ self.counters[collection].add(value)
+
+ def get_count(self, collection: str) -> int:
+ return len(self.counters.get(collection, []))
+
+ def get_counts(self, collections: List[str]) -> Dict[str, int]:
+ return {coll: self.get_count(coll) for coll in collections}
+
+ def get_counters(self) -> Iterable[str]:
+ return list(self.counters.keys())
diff --git a/swh/counters/tests/test_init.py b/swh/counters/tests/test_init.py
index 18d38d5..1eb5915 100644
--- a/swh/counters/tests/test_init.py
+++ b/swh/counters/tests/test_init.py
@@ -1,90 +1,92 @@
# Copyright (C) 2021 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
import inspect
import pytest
from swh.counters import get_counters, get_history
from swh.counters.api.client import RemoteCounters
from swh.counters.history import History
+from swh.counters.in_memory import InMemory
from swh.counters.interface import CountersInterface
from swh.counters.redis import Redis
COUNTERS_IMPLEMENTATIONS = [
("remote", RemoteCounters, {"url": "localhost"}),
("redis", Redis, {"host": "localhost"}),
+ ("memory", InMemory, {}),
]
def test_get_counters_failure():
with pytest.raises(ValueError, match="Unknown counters class"):
get_counters("unknown-counters")
@pytest.mark.parametrize("class_,expected_class,kwargs", COUNTERS_IMPLEMENTATIONS)
def test_get_counters(mocker, class_, expected_class, kwargs):
if kwargs:
concrete_counters = get_counters(class_, **kwargs)
else:
concrete_counters = get_counters(class_)
assert isinstance(concrete_counters, expected_class)
@pytest.mark.parametrize("class_,expected_class,kwargs", COUNTERS_IMPLEMENTATIONS)
def test_types(mocker, class_, expected_class, kwargs):
"""Checks all methods of CountersInterface are implemented by this
backend, and that they have the same signature.
"""
# mocker.patch("swh.counters.redis.Redis")
if kwargs:
concrete_counters = get_counters(class_, **kwargs)
else:
concrete_counters = get_counters(class_)
# Create an instance of the protocol (which cannot be instantiated
# directly, so this creates a subclass, then instantiates it)
interface = type("_", (CountersInterface,), {})()
for meth_name in dir(interface):
if meth_name.startswith("_"):
continue
interface_meth = getattr(interface, meth_name)
missing_methods = []
try:
concrete_meth = getattr(concrete_counters, meth_name)
except AttributeError:
if not getattr(interface_meth, "deprecated_endpoint", False):
# The backend is missing a (non-deprecated) endpoint
missing_methods.append(meth_name)
continue
expected_signature = inspect.signature(interface_meth)
actual_signature = inspect.signature(concrete_meth)
assert expected_signature == actual_signature, meth_name
assert missing_methods == []
def test_get_history_failure():
with pytest.raises(ValueError, match="Unknown history class"):
get_history("unknown-history")
def test_get_history():
concrete_history = get_history(
"prometheus",
**{
"prometheus_host": "",
"prometheus_port": "",
"live_data_start": "",
"cache_base_directory": "",
},
)
assert isinstance(concrete_history, History)
diff --git a/swh/counters/tests/test_inmemory.py b/swh/counters/tests/test_inmemory.py
new file mode 100644
index 0000000..1380350
--- /dev/null
+++ b/swh/counters/tests/test_inmemory.py
@@ -0,0 +1,54 @@
+# Copyright (C) 2021 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from swh.counters.in_memory import InMemory
+
+
+def test__inmemory__add():
+ im = InMemory()
+
+ im.add("counter1", ["val1"])
+ im.add("counter2", ["val1"])
+ im.add("counter1", [1])
+
+ assert im.get_count("counter1") == 2
+ assert im.get_count("counter2") == 1
+ assert im.get_count("inexisting") == 0
+
+
+def test__inmemory_getcounters():
+ im = InMemory()
+
+ assert len(im.get_counters()) == 0
+
+ counters = ["c1", "c2", "c3"]
+
+ count = 0
+
+ for counter in counters:
+ im.add(counter, [1, 2])
+ count += 1
+ assert count == len(im.get_counters())
+
+ results = im.get_counters()
+ assert results == counters
+
+
+def test__inmemory_getcounts():
+ im = InMemory()
+
+ expected = {"c1": 1, "c2": 2, "c3": 0}
+
+ im.add("c1", ["v1"])
+ im.add("c2", ["v1", "v2"])
+
+ result = im.get_counts(["c1", "c2", "c3"])
+ assert result == expected
+
+
+def test__inmemory_check():
+ im = InMemory()
+
+ assert im.check() == "OK"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Jul 4 2025, 7:46 AM (10 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3236140
Attached To
rDCNT Archive counters
Event Timeline
Log In to Comment