All checks were successful
buildbot/nix-build .#checks.x86_64-linux.clan-dep-e2fsprogs Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-bash Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-app-no-breakpoints Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-fakeroot Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-age Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.check-for-breakpoints Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-app-pytest Build done.
buildbot/nix-build .#checks.x86_64-linux.borgbackup Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-test-backup Build done.
buildbot/nix-build .#checks.aarch64-darwin.nixos-flash-installer Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.aarch64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-git Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-openssh Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-mypy" Build done.
buildbot/nix-build .#checks.x86_64-linux."clan-dep-python3.11-qemu" Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-rsync Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sops Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-sshpass Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-zbar Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-without-core Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-pytest-with-core Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-cli Build done.
buildbot/nix-build .#checks.x86_64-linux.deltachat Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-default Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test_install_machine Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-tor Build done.
buildbot/nix-build .#checks.x86_64-linux.devShell-clan-app Build done.
buildbot/nix-build .#checks.x86_64-linux.clan-dep-nix Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-example-valid Build done.
buildbot/nix-build .#checks.x86_64-linux.container Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-app Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-cli-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-clan-ts-api Build done.
buildbot/nix-build .#checks.x86_64-linux.package-default Build done.
buildbot/nix-build .#checks.x86_64-linux.matrix-synapse Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-install-test-ubuntu-22-04 Build done.
buildbot/nix-build .#checks.x86_64-linux.test-backups Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-apk Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-deb Build done.
buildbot/nix-build .#checks.x86_64-linux.module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.package-editor Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-archlinux Build done.
buildbot/nix-build .#checks.x86_64-linux.package-gui-installer-rpm Build done.
buildbot/nix-build .#checks.x86_64-linux.package-impure-checks Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-test-backup Build done.
buildbot/nix-build .#checks.x86_64-linux.lib-jsonschema-nix-unit-tests Build done.
buildbot/nix-build .#checks.x86_64-linux.package-deploy-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.package-moonlight-sunshine-accept Build done.
buildbot/nix-build .#checks.x86_64-linux.package-pending-reviews Build done.
buildbot/nix-build .#checks.x86_64-linux.package-tea-create-pr Build done.
buildbot/nix-build .#checks.x86_64-linux.package-webview-ui Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotier-members Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zerotierone Build done.
buildbot/nix-build .#checks.x86_64-linux.package-zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.nixos-flash-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.secrets Build done.
buildbot/nix-build .#checks.x86_64-linux.treefmt Build done.
buildbot/nix-build .#checks.x86_64-linux.package-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.renderClanOptions Build done.
buildbot/nix-build .#checks.x86_64-linux.package-function-schema Build done.
buildbot/nix-build .#checks.x86_64-linux.package-merge-after-ci Build done.
buildbot/nix-build .#checks.x86_64-linux.package-module-docs Build done.
buildbot/nix-build .#checks.x86_64-linux.wayland-proxy-virtwl Build done.
buildbot/nix-build .#checks.x86_64-linux.zt-tcp-relay Build done.
buildbot/nix-build .#checks.x86_64-linux.package-iso-installer Build done.
buildbot/nix-build .#checks.x86_64-linux.syncthing Build done.
checks / checks-impure (pull_request) Successful in 2m19s
buildbot/nix-build .#checks.x86_64-linux.flash Build done.
buildbot/nix-build .#checks.x86_64-linux.test-installation Build done.
buildbot/nix-eval Build done.
221 lines
7.3 KiB
Python
221 lines
7.3 KiB
Python
import logging
|
|
from collections.abc import Callable
|
|
from typing import Any, Generic, TypeVar
|
|
|
|
import gi
|
|
|
|
gi.require_version("Gio", "2.0")
|
|
from gi.repository import Gio, GObject
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
# Define type variables for key and value types
|
|
K = TypeVar("K") # Key type
|
|
V = TypeVar(
|
|
"V", bound=GObject.Object
|
|
) # Value type, bound to GObject.GObject or its subclasses
|
|
|
|
|
|
class GKVStore(GObject.GObject, Gio.ListModel, Generic[K, V]):
|
|
"""
|
|
A simple key-value store that implements the Gio.ListModel interface, with generic types for keys and values.
|
|
Only use self[key] and del self[key] for accessing the items for better performance.
|
|
This class could be optimized by having the objects remember their position in the list.
|
|
"""
|
|
|
|
def __init__(self, gtype: type[V], key_gen: Callable[[V], K]) -> None:
|
|
super().__init__()
|
|
self.gtype = gtype
|
|
self.key_gen = key_gen
|
|
# From Python 3.7 onwards dictionaries are ordered by default
|
|
self._items: dict[K, V] = dict()
|
|
|
|
##################################
|
|
# #
|
|
# Gio.ListStore Interface #
|
|
# #
|
|
##################################
|
|
@classmethod
|
|
def new(cls: Any, gtype: type[V]) -> "GKVStore":
|
|
return cls.__new__(cls, gtype)
|
|
|
|
def append(self, item: V) -> None:
|
|
key = self.key_gen(item)
|
|
self[key] = item
|
|
|
|
def find(self, item: V) -> tuple[bool, int]:
|
|
log.warning("Finding is O(n) in GKVStore. Better use indexing")
|
|
for i, v in enumerate(self.values()):
|
|
if v == item:
|
|
return True, i
|
|
return False, -1
|
|
|
|
def find_with_equal_func(
|
|
self, item: V, equal_func: Callable[[V, V], bool]
|
|
) -> tuple[bool, int]:
|
|
log.warning("Finding is O(n) in GKVStore. Better use indexing")
|
|
for i, v in enumerate(self.values()):
|
|
if equal_func(v, item):
|
|
return True, i
|
|
return False, -1
|
|
|
|
def find_with_equal_func_full(
|
|
self, item: V, equal_func: Callable[[V, V, Any], bool], user_data: Any
|
|
) -> tuple[bool, int]:
|
|
log.warning("Finding is O(n) in GKVStore. Better use indexing")
|
|
for i, v in enumerate(self.values()):
|
|
if equal_func(v, item, user_data):
|
|
return True, i
|
|
return False, -1
|
|
|
|
def insert(self, position: int, item: V) -> None:
|
|
log.warning("Inserting is O(n) in GKVStore. Better use append")
|
|
log.warning(
|
|
"This functions may have incorrect items_changed signal behavior. Please test it"
|
|
)
|
|
key = self.key_gen(item)
|
|
if key in self._items:
|
|
raise ValueError("Key already exists in the dictionary")
|
|
if position < 0 or position > len(self._items):
|
|
raise IndexError("Index out of range")
|
|
|
|
# Temporary storage for items to be reinserted
|
|
temp_list = [(k, self._items[k]) for k in list(self.keys())[position:]]
|
|
|
|
# Delete items from the original dict
|
|
for k in list(self.keys())[position:]:
|
|
del self._items[k]
|
|
|
|
# Insert the new key-value pair
|
|
self._items[key] = item
|
|
|
|
# Reinsert the items
|
|
for i, (k, v) in enumerate(temp_list):
|
|
self._items[k] = v
|
|
|
|
# Notify the model of the changes
|
|
self.items_changed(position, 0, 1)
|
|
|
|
def insert_sorted(
|
|
self, item: V, compare_func: Callable[[V, V, Any], int], user_data: Any
|
|
) -> None:
|
|
raise NotImplementedError("insert_sorted is not implemented in GKVStore")
|
|
|
|
def remove(self, position: int) -> None:
|
|
if position < 0 or position >= self.get_n_items():
|
|
return
|
|
key = self.keys()[position]
|
|
del self[key]
|
|
self.items_changed(position, 1, 0)
|
|
|
|
def remove_all(self) -> None:
|
|
self._items.clear()
|
|
self.items_changed(0, len(self._items), 0)
|
|
|
|
def sort(self, compare_func: Callable[[V, V, Any], int], user_data: Any) -> None:
|
|
raise NotImplementedError("sort is not implemented in GKVStore")
|
|
|
|
def splice(self, position: int, n_removals: int, additions: list[V]) -> None:
|
|
raise NotImplementedError("splice is not implemented in GKVStore")
|
|
|
|
##################################
|
|
# #
|
|
# Gio.ListModel Interface #
|
|
# #
|
|
##################################
|
|
def get_item(self, position: int) -> V | None:
|
|
if position < 0 or position >= self.get_n_items():
|
|
return None
|
|
# Access items by index since OrderedDict does not support direct indexing
|
|
key = list(self._items.keys())[position]
|
|
return self._items[key]
|
|
|
|
def do_get_item(self, position: int) -> V | None:
|
|
return self.get_item(position)
|
|
|
|
def get_item_type(self) -> Any:
|
|
return self.gtype.__gtype__ # type: ignore[attr-defined]
|
|
|
|
def do_get_item_type(self) -> GObject.GType:
|
|
return self.get_item_type()
|
|
|
|
def get_n_items(self) -> int:
|
|
return len(self._items)
|
|
|
|
def do_get_n_items(self) -> int:
|
|
return self.get_n_items()
|
|
|
|
##################################
|
|
# #
|
|
# Dict Interface #
|
|
# #
|
|
##################################
|
|
def keys(self) -> list[K]:
|
|
return list(self._items.keys())
|
|
|
|
def values(self) -> list[V]:
|
|
return list(self._items.values())
|
|
|
|
def items(self) -> list[tuple[K, V]]:
|
|
return list(self._items.items())
|
|
|
|
def get(self, key: K, default: V | None = None) -> V | None:
|
|
return self._items.get(key, default)
|
|
|
|
# O(1) operation if the key does not exist, O(n) if it does
|
|
def __setitem__(self, key: K, value: V) -> None:
|
|
# If the key already exists, remove it O(n)
|
|
if key in self._items:
|
|
log.debug("Updating an existing key in GKVStore is O(n)")
|
|
position = self.keys().index(key)
|
|
self._items[key] = value
|
|
self.items_changed(position, 1, 1)
|
|
else:
|
|
# Add the new key-value pair
|
|
self._items[key] = value
|
|
position = max(len(self._items) - 1, 0)
|
|
self.items_changed(position, 0, 1)
|
|
|
|
# O(n) operation
|
|
def __delitem__(self, key: K) -> None:
|
|
position = self.keys().index(key)
|
|
del self._items[key]
|
|
self.items_changed(position, 1, 0)
|
|
|
|
def __len__(self) -> int:
|
|
return len(self._items)
|
|
|
|
# O(1) operation
|
|
def __getitem__(self, key: K) -> V: # type: ignore[override]
|
|
return self._items[key]
|
|
|
|
def __contains__(self, key: K) -> bool: # type: ignore[override]
|
|
return key in self._items
|
|
|
|
def __str__(self) -> str:
|
|
resp = "GKVStore(\n"
|
|
for k, v in self._items.items():
|
|
resp += f"{k}: {v}\n"
|
|
resp += ")"
|
|
return resp
|
|
|
|
def __repr__(self) -> str:
|
|
return self._items.__str__()
|
|
|
|
##################################
|
|
# #
|
|
# Custom Methods #
|
|
# #
|
|
##################################
|
|
def first(self) -> V:
|
|
return self.values()[0]
|
|
|
|
def last(self) -> V:
|
|
return self.values()[-1]
|
|
|
|
def register_on_change(
|
|
self, callback: Callable[["GKVStore[K,V]", int, int, int], None]
|
|
) -> None:
|
|
self.connect("items-changed", callback)
|