Skip to content

Commit 856d30b

Browse files
committed
Add Python coverage for FFI tier-store builders
Add a Python in-memory KV store implementation that satisfies the UniFFI store callback interface, including both sync and async accessors. Refactor the Python integration test setup so channel lifecycle tests can share node construction, cleanup, funding, channel open, payment, and close logic. Add a tier-store test that exercises the FFI builder path with Python primary and ephemeral stores while configuring the backup through the builder's SQLite backup path. The test verifies that Python-backed stores remain usable throughout a full channel lifecycle and that ephemeral data is routed to the ephemeral store.
1 parent 9cdf7a0 commit 856d30b

2 files changed

Lines changed: 344 additions & 93 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import threading
2+
3+
from abc import ABC, abstractmethod
4+
from typing import List
5+
6+
from ldk_node import IoError
7+
8+
class AbstractKvStore(ABC):
9+
@abstractmethod
10+
async def read_async(self, primary_namespace: "str",secondary_namespace: "str",key: "str") -> "List[int]":
11+
pass
12+
13+
@abstractmethod
14+
async def write_async(self, primary_namespace: "str",secondary_namespace: "str",key: "str",buf: "List[int]") -> None:
15+
pass
16+
17+
@abstractmethod
18+
async def remove_async(self, primary_namespace: "str",secondary_namespace: "str",key: "str",lazy: "bool") -> None:
19+
pass
20+
21+
@abstractmethod
22+
async def list_async(self, primary_namespace: "str",secondary_namespace: "str") -> "List[str]":
23+
pass
24+
25+
@abstractmethod
26+
def read(self, primary_namespace: "str",secondary_namespace: "str",key: "str") -> "List[int]":
27+
pass
28+
29+
@abstractmethod
30+
def write(self, primary_namespace: "str",secondary_namespace: "str",key: "str",buf: "List[int]") -> None:
31+
pass
32+
33+
@abstractmethod
34+
def remove(self, primary_namespace: "str",secondary_namespace: "str",key: "str",lazy: "bool") -> None:
35+
pass
36+
37+
@abstractmethod
38+
def list(self, primary_namespace: "str",secondary_namespace: "str") -> "List[str]":
39+
pass
40+
41+
class TestKvStore(AbstractKvStore):
42+
def __init__(self, name: str):
43+
self.name = name
44+
# Storage structure: {(primary_ns, secondary_ns): {key: [bytes]}}
45+
self.storage = {}
46+
self._lock = threading.Lock()
47+
48+
def dump(self):
49+
print(f"\n[{self.name}] Store contents:")
50+
for (primary_ns, secondary_ns), keys_dict in self.storage.items():
51+
print(f" Namespace: ({primary_ns!r}, {secondary_ns!r})")
52+
for key, data in keys_dict.items():
53+
print(f" Key: {key!r} -> {len(data)} bytes")
54+
# Optionally show first few bytes
55+
preview = data[:20] if len(data) > 20 else data
56+
print(f" Data preview: {preview}...")
57+
58+
def read(self, primary_namespace: str, secondary_namespace: str, key: str) -> List[int]:
59+
with self._lock:
60+
namespace_key = (primary_namespace, secondary_namespace)
61+
62+
if namespace_key not in self.storage:
63+
raise IoError.NotFound()
64+
65+
if key not in self.storage[namespace_key]:
66+
raise IoError.NotFound()
67+
68+
return list(self.storage[namespace_key][key])
69+
70+
def write(self, primary_namespace: str, secondary_namespace: str, key: str, buf: List[int]) -> None:
71+
with self._lock:
72+
namespace_key = (primary_namespace, secondary_namespace)
73+
if namespace_key not in self.storage:
74+
self.storage[namespace_key] = {}
75+
76+
self.storage[namespace_key][key] = list(buf)
77+
78+
def remove(self, primary_namespace: str, secondary_namespace: str, key: str, lazy: bool) -> None:
79+
with self._lock:
80+
namespace_key = (primary_namespace, secondary_namespace)
81+
if namespace_key not in self.storage:
82+
raise IoError.NotFound()
83+
84+
if key not in self.storage[namespace_key]:
85+
raise IoError.NotFound()
86+
87+
del self.storage[namespace_key][key]
88+
89+
if not self.storage[namespace_key]:
90+
del self.storage[namespace_key]
91+
92+
def list(self, primary_namespace: str, secondary_namespace: str) -> List[str]:
93+
with self._lock:
94+
namespace_key = (primary_namespace, secondary_namespace)
95+
if namespace_key in self.storage:
96+
return sorted(self.storage[namespace_key].keys())
97+
return []
98+
99+
async def read_async(self, primary_namespace: str, secondary_namespace: str, key: str) -> List[int]:
100+
return self.read(primary_namespace, secondary_namespace, key)
101+
102+
async def write_async(self, primary_namespace: str, secondary_namespace: str, key: str, buf: List[int]) -> None:
103+
self.write(primary_namespace, secondary_namespace, key, buf)
104+
105+
async def remove_async(self, primary_namespace: str, secondary_namespace: str, key: str, lazy: bool) -> None:
106+
self.remove(primary_namespace, secondary_namespace, key, lazy)
107+
108+
async def list_async(self, primary_namespace: str, secondary_namespace: str) -> List[str]:
109+
return self.list(primary_namespace, secondary_namespace)
110+

0 commit comments

Comments
 (0)