mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-08-02 03:36:07 -04:00
clean up veilid-python
This commit is contained in:
parent
9d4976b243
commit
9f0495cacb
5 changed files with 145 additions and 43 deletions
|
@ -135,6 +135,8 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
|
|||
key = rec.key
|
||||
owner = rec.owner
|
||||
secret = rec.owner_secret
|
||||
assert secret is not None
|
||||
|
||||
#print(f"key:{key}")
|
||||
|
||||
cs = await api_connection.get_crypto_system(rec.key.kind())
|
||||
|
@ -151,6 +153,7 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
|
|||
assert vdtemp is None
|
||||
|
||||
vdtemp = await rc.get_dht_value(key, ValueSubkey(1), False)
|
||||
assert vdtemp is not None
|
||||
assert vdtemp.data == va
|
||||
assert vdtemp.seq == 0
|
||||
assert vdtemp.writer == owner
|
||||
|
@ -164,9 +167,11 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
|
|||
await sync(rc, [rec])
|
||||
|
||||
vdtemp = await rc.get_dht_value(key, ValueSubkey(0), True)
|
||||
assert vdtemp is not None
|
||||
assert vdtemp.data == vb
|
||||
|
||||
vdtemp = await rc.get_dht_value(key, ValueSubkey(1), True)
|
||||
assert vdtemp is not None
|
||||
assert vdtemp.data == va
|
||||
|
||||
# Equal value should not trigger sequence number update
|
||||
|
@ -191,7 +196,7 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
|
|||
assert rec.key == key
|
||||
assert rec.owner == owner
|
||||
assert rec.owner_secret == secret
|
||||
assert rec.schema.kind == veilid.DHTSchemaKind.DFLT
|
||||
assert isinstance(rec.schema, veilid.DHTSchemaDFLT)
|
||||
assert rec.schema.o_cnt == 2
|
||||
|
||||
# Verify subkey 1 can be set before it is get but newer is available online
|
||||
|
@ -225,7 +230,7 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
|
|||
assert rec.key == key
|
||||
assert rec.owner == owner
|
||||
assert rec.owner_secret is None
|
||||
assert rec.schema.kind == veilid.DHTSchemaKind.DFLT
|
||||
assert isinstance(rec.schema, veilid.DHTSchemaDFLT)
|
||||
assert rec.schema.o_cnt == 2
|
||||
|
||||
# Verify subkey 1 can NOT be set because we have the wrong writer
|
||||
|
@ -261,6 +266,7 @@ async def test_open_writer_dht_value_no_offline(api_connection: veilid.VeilidAPI
|
|||
key = rec.key
|
||||
owner = rec.owner
|
||||
secret = rec.owner_secret
|
||||
assert secret is not None
|
||||
#print(f"key:{key}")
|
||||
|
||||
cs = await api_connection.get_crypto_system(rec.key.kind())
|
||||
|
@ -277,6 +283,7 @@ async def test_open_writer_dht_value_no_offline(api_connection: veilid.VeilidAPI
|
|||
assert vdtemp is None
|
||||
|
||||
vdtemp = await rc.get_dht_value(key, ValueSubkey(1), False)
|
||||
assert vdtemp is not None
|
||||
assert vdtemp.data == va
|
||||
assert vdtemp.seq == 0
|
||||
assert vdtemp.writer == owner
|
||||
|
@ -287,12 +294,12 @@ async def test_open_writer_dht_value_no_offline(api_connection: veilid.VeilidAPI
|
|||
vdtemp = await rc.set_dht_value(key, ValueSubkey(0), vb, veilid.SetDHTValueOptions(None, False))
|
||||
assert vdtemp is None
|
||||
|
||||
await sync(rc, [rec])
|
||||
|
||||
vdtemp = await rc.get_dht_value(key, ValueSubkey(0), True)
|
||||
assert vdtemp is not None
|
||||
assert vdtemp.data == vb
|
||||
|
||||
vdtemp = await rc.get_dht_value(key, ValueSubkey(1), True)
|
||||
assert vdtemp is not None
|
||||
assert vdtemp.data == va
|
||||
|
||||
# Equal value should not trigger sequence number update
|
||||
|
@ -316,7 +323,7 @@ async def test_open_writer_dht_value_no_offline(api_connection: veilid.VeilidAPI
|
|||
assert rec.key == key
|
||||
assert rec.owner == owner
|
||||
assert rec.owner_secret == secret
|
||||
assert rec.schema.kind == veilid.DHTSchemaKind.DFLT
|
||||
assert isinstance(rec.schema, veilid.DHTSchemaDFLT)
|
||||
assert rec.schema.o_cnt == 2
|
||||
|
||||
# Verify subkey 1 can be set before it is get but newer is available online
|
||||
|
@ -348,7 +355,7 @@ async def test_open_writer_dht_value_no_offline(api_connection: veilid.VeilidAPI
|
|||
assert rec.key == key
|
||||
assert rec.owner == owner
|
||||
assert rec.owner_secret is None
|
||||
assert rec.schema.kind == veilid.DHTSchemaKind.DFLT
|
||||
assert isinstance(rec.schema, veilid.DHTSchemaDFLT)
|
||||
assert rec.schema.o_cnt == 2
|
||||
|
||||
# Verify subkey 1 can NOT be set because we have the wrong writer
|
||||
|
@ -457,9 +464,11 @@ async def test_watch_dht_values():
|
|||
upd = await asyncio.wait_for(value_change_queue.get(), timeout=10)
|
||||
|
||||
# Server 0: Verify the update came back with the first changed subkey's data
|
||||
assert isinstance(upd.detail, veilid.VeilidValueChange)
|
||||
assert upd.detail.key == rec0.key
|
||||
assert upd.detail.count == 0xFFFFFFFE
|
||||
assert upd.detail.subkeys == [(3, 3)]
|
||||
assert upd.detail.value is not None
|
||||
assert upd.detail.value.data == b"BLAH BLAH"
|
||||
|
||||
# Server 1: Now set subkey and trigger an update
|
||||
|
@ -471,9 +480,11 @@ async def test_watch_dht_values():
|
|||
upd = await asyncio.wait_for(value_change_queue.get(), timeout=10)
|
||||
|
||||
# Server 0: Verify the update came back with the first changed subkey's data
|
||||
assert isinstance(upd.detail, veilid.VeilidValueChange)
|
||||
assert upd.detail.key == rec0.key
|
||||
assert upd.detail.count == 0xFFFFFFFD
|
||||
assert upd.detail.subkeys == [(4, 4)]
|
||||
assert upd.detail.value is not None
|
||||
assert upd.detail.value.data == b"BZORT"
|
||||
|
||||
# Server 0: Cancel some subkeys we don't care about
|
||||
|
@ -489,9 +500,11 @@ async def test_watch_dht_values():
|
|||
upd = await asyncio.wait_for(value_change_queue.get(), timeout=10)
|
||||
|
||||
# Server 0: Verify only one update came back
|
||||
assert isinstance(upd.detail, veilid.VeilidValueChange)
|
||||
assert upd.detail.key == rec0.key
|
||||
assert upd.detail.count == 0xFFFFFFFC
|
||||
assert upd.detail.subkeys == [(4, 4)]
|
||||
assert upd.detail.value is not None
|
||||
assert upd.detail.value.data == b"BZORT BZORT"
|
||||
|
||||
# Server 0: Now we should NOT get any other update
|
||||
|
@ -512,6 +525,7 @@ async def test_watch_dht_values():
|
|||
upd = await asyncio.wait_for(value_change_queue.get(), timeout=10)
|
||||
|
||||
# Server 0: Verify only one update came back
|
||||
assert isinstance(upd.detail, veilid.VeilidValueChange)
|
||||
assert upd.detail.key == rec0.key
|
||||
assert upd.detail.count == 0
|
||||
assert upd.detail.subkeys == []
|
||||
|
@ -618,6 +632,7 @@ async def test_watch_many_dht_values():
|
|||
# Server 0: Wait for the update
|
||||
try:
|
||||
upd = await asyncio.wait_for(value_change_queue.get(), timeout=10)
|
||||
assert isinstance(upd.detail, veilid.VeilidValueChange)
|
||||
missing_records.remove(upd.detail.key)
|
||||
except:
|
||||
# Dump which records didn't get updates
|
||||
|
@ -689,6 +704,7 @@ async def _run_test_schema_limit(api_connection: veilid.VeilidAPI, open_record:
|
|||
desc1 = await rc.open_dht_record(desc.key)
|
||||
for n in range(count):
|
||||
vd0 = await rc.get_dht_value(desc1.key, ValueSubkey(n))
|
||||
assert vd0 is not None
|
||||
assert vd0.data == test_data
|
||||
print(f' {n}')
|
||||
|
||||
|
@ -728,7 +744,7 @@ async def test_schema_limit_dflt(api_connection: veilid.VeilidAPI):
|
|||
@pytest.mark.asyncio
|
||||
async def test_schema_limit_smpl(api_connection: veilid.VeilidAPI):
|
||||
|
||||
async def open_record(rc: veilid.RoutingContext, count: int) -> tuple[veilid.TypedKey, veilid.PublicKey, veilid.SecretKey]:
|
||||
async def open_record(rc: veilid.RoutingContext, count: int) -> tuple[veilid.DHTRecordDescriptor, veilid.KeyPair]:
|
||||
cs = await api_connection.best_crypto_system()
|
||||
async with cs:
|
||||
writer_keypair = await cs.generate_key_pair()
|
||||
|
@ -817,6 +833,7 @@ async def test_dht_integration_writer_reader():
|
|||
|
||||
desc1 = await rc1.open_dht_record(desc.key)
|
||||
vd1 = await rc1.get_dht_value(desc1.key, ValueSubkey(0))
|
||||
assert vd1 is not None
|
||||
assert vd1.data == TEST_DATA
|
||||
await rc1.close_dht_record(desc1.key)
|
||||
|
||||
|
@ -877,9 +894,11 @@ async def test_dht_write_read_local():
|
|||
desc1 = await rc0.open_dht_record(desc0.key)
|
||||
|
||||
vd0 = await rc0.get_dht_value(desc1.key, ValueSubkey(0), force_refresh=True)
|
||||
assert vd0 is not None
|
||||
assert vd0.data == TEST_DATA
|
||||
|
||||
vd1 = await rc0.get_dht_value(desc1.key, ValueSubkey(1), force_refresh=True)
|
||||
assert vd1 is not None
|
||||
assert vd1.data == TEST_DATA2
|
||||
await rc0.close_dht_record(desc1.key)
|
||||
|
||||
|
@ -1082,8 +1101,8 @@ async def sync_win(
|
|||
max(0, int(cur_cols/2) - int(WIDTH/2)))
|
||||
win.border(0,0,0,0)
|
||||
win.addstr(1, 1, "syncing records to the network", curses.color_pair(0))
|
||||
for n, rr in enumerate(records):
|
||||
key = rr.key
|
||||
for n, rd in enumerate(records):
|
||||
key = rd.key
|
||||
win.addstr(n+2, GRAPHWIDTH+1, key, curses.color_pair(0))
|
||||
|
||||
if key in donerecords:
|
||||
|
|
|
@ -49,7 +49,7 @@ class RoutingContext(ABC):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def app_call(self, target: types.TypedKey | types.RouteId, request: bytes) -> bytes:
|
||||
async def app_call(self, target: types.TypedKey | types.RouteId, message: bytes) -> bytes:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
@ -132,7 +132,7 @@ class TableDbTransaction(ABC):
|
|||
async def __aexit__(self, *excinfo):
|
||||
self.ref_count -= 1
|
||||
if self.ref_count == 0 and not self.is_done():
|
||||
await self.release()
|
||||
await self.rollback()
|
||||
|
||||
@abstractmethod
|
||||
def is_done(self) -> bool:
|
||||
|
|
|
@ -522,7 +522,7 @@ class VeilidAppCall:
|
|||
message: bytes
|
||||
call_id: OperationId
|
||||
|
||||
def __init__(self, sender: Optional[TypedKey], route_id: Optional[TypedKey], message: bytes, call_id: OperationId):
|
||||
def __init__(self, sender: Optional[TypedKey], route_id: Optional[RouteId], message: bytes, call_id: OperationId):
|
||||
self.sender = sender
|
||||
self.route_id = route_id
|
||||
self.message = message
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import base64
|
||||
import json
|
||||
from abc import ABC, abstractmethod
|
||||
from enum import StrEnum
|
||||
from functools import total_ordering
|
||||
from typing import Any, Optional, Self
|
||||
from typing import Any, Optional, Self, final
|
||||
|
||||
####################################################################
|
||||
|
||||
|
@ -267,9 +268,9 @@ class VeilidVersion:
|
|||
def __eq__(self, other):
|
||||
return (
|
||||
isinstance(other, VeilidVersion)
|
||||
and self.data == other.data
|
||||
and self.seq == other.seq
|
||||
and self.writer == other.writer
|
||||
and self._major == other._major
|
||||
and self._minor == other._minor
|
||||
and self._patch == other._patch
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -323,26 +324,19 @@ class DHTSchemaSMPLMember:
|
|||
return self.__dict__
|
||||
|
||||
|
||||
class DHTSchema:
|
||||
class DHTSchema(ABC):
|
||||
kind: DHTSchemaKind
|
||||
|
||||
def __init__(self, kind: DHTSchemaKind, **kwargs):
|
||||
def __init__(self, kind: DHTSchemaKind):
|
||||
self.kind = kind
|
||||
for k, v in kwargs.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
@classmethod
|
||||
def dflt(cls, o_cnt: int) -> Self:
|
||||
assert isinstance(o_cnt, int)
|
||||
return cls(DHTSchemaKind.DFLT, o_cnt=o_cnt)
|
||||
return DHTSchemaDFLT(o_cnt=o_cnt) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def smpl(cls, o_cnt: int, members: list[DHTSchemaSMPLMember]) -> Self:
|
||||
assert isinstance(o_cnt, int)
|
||||
assert isinstance(members, list)
|
||||
for m in members:
|
||||
assert isinstance(m, DHTSchemaSMPLMember)
|
||||
return cls(DHTSchemaKind.SMPL, o_cnt=o_cnt, members=members)
|
||||
return DHTSchemaSMPL(o_cnt=o_cnt, members=members) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, j: dict) -> Self:
|
||||
|
@ -358,6 +352,53 @@ class DHTSchema:
|
|||
def to_json(self) -> dict:
|
||||
return self.__dict__
|
||||
|
||||
@final
|
||||
class DHTSchemaDFLT(DHTSchema):
|
||||
o_cnt: int
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
o_cnt: int
|
||||
):
|
||||
super().__init__(DHTSchemaKind.DFLT)
|
||||
|
||||
assert isinstance(o_cnt, int)
|
||||
self.o_cnt = o_cnt
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, j: dict) -> Self:
|
||||
if DHTSchemaKind(j["kind"]) == DHTSchemaKind.DFLT:
|
||||
return cls(j["o_cnt"])
|
||||
raise Exception("Invalid DHTSchemaDFLT")
|
||||
|
||||
|
||||
@final
|
||||
class DHTSchemaSMPL(DHTSchema):
|
||||
o_cnt: int
|
||||
members: list[DHTSchemaSMPLMember]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
o_cnt: int,
|
||||
members: list[DHTSchemaSMPLMember]
|
||||
):
|
||||
super().__init__(DHTSchemaKind.SMPL)
|
||||
|
||||
assert isinstance(o_cnt, int)
|
||||
assert isinstance(members, list)
|
||||
for m in members:
|
||||
assert isinstance(m, DHTSchemaSMPLMember)
|
||||
|
||||
self.o_cnt = o_cnt
|
||||
self.members = members
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, j: dict) -> Self:
|
||||
if DHTSchemaKind(j["kind"]) == DHTSchemaKind.SMPL:
|
||||
return cls(j["o_cnt"],
|
||||
[DHTSchemaSMPLMember.from_json(member) for member in j["members"]])
|
||||
raise Exception("Invalid DHTSchemaSMPL")
|
||||
|
||||
class DHTRecordDescriptor:
|
||||
key: TypedKey
|
||||
|
@ -381,6 +422,8 @@ class DHTRecordDescriptor:
|
|||
return f"<{self.__class__.__name__}(key={self.key!r}, owner={self.owner!r}, owner_secret={self.owner_secret!r}, schema={self.schema!r})>"
|
||||
|
||||
def owner_key_pair(self) -> Optional[KeyPair]:
|
||||
if self.owner_secret is None:
|
||||
return None
|
||||
return KeyPair.from_parts(self.owner, self.owner_secret)
|
||||
|
||||
@classmethod
|
||||
|
@ -435,7 +478,7 @@ class SetDHTValueOptions:
|
|||
writer: Optional[KeyPair]
|
||||
allow_offline: Optional[bool]
|
||||
|
||||
def __init__(self, writer: Optional[KeyPair], allow_offline: Optional[bool] = None):
|
||||
def __init__(self, writer: Optional[KeyPair] = None, allow_offline: Optional[bool] = None):
|
||||
self.writer = writer
|
||||
self.allow_offline = allow_offline
|
||||
|
||||
|
@ -534,22 +577,20 @@ class SafetySpec:
|
|||
def to_json(self) -> dict:
|
||||
return self.__dict__
|
||||
|
||||
class SafetySelection(ABC):
|
||||
|
||||
class SafetySelection:
|
||||
kind: SafetySelectionKind
|
||||
|
||||
def __init__(self, kind: SafetySelectionKind, **kwargs):
|
||||
self.kind = kind
|
||||
for k, v in kwargs.items():
|
||||
setattr(self, k, v)
|
||||
@property
|
||||
@abstractmethod
|
||||
def kind(self) -> SafetySelectionKind:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def unsafe(cls, sequencing: Sequencing = Sequencing.PREFER_ORDERED) -> Self:
|
||||
return cls(SafetySelectionKind.UNSAFE, sequencing=sequencing)
|
||||
return SafetySelectionUnsafe(sequencing=sequencing) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def safe(cls, safety_spec: SafetySpec) -> Self:
|
||||
return cls(SafetySelectionKind.SAFE, safety_spec=safety_spec)
|
||||
return SafetySelectionSafe(safety_spec=safety_spec) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, j: dict) -> Self:
|
||||
|
@ -559,10 +600,48 @@ class SafetySelection:
|
|||
return cls.unsafe(Sequencing(j["Unsafe"]))
|
||||
raise Exception("Invalid SafetySelection")
|
||||
|
||||
@abstractmethod
|
||||
def to_json(self) -> dict:
|
||||
if self.kind == SafetySelectionKind.UNSAFE:
|
||||
return {"Unsafe": self.sequencing}
|
||||
elif self.kind == SafetySelectionKind.SAFE:
|
||||
return {"Safe": self.safety_spec.to_json()}
|
||||
else:
|
||||
raise Exception("Invalid SafetySelection")
|
||||
pass
|
||||
|
||||
@final
|
||||
class SafetySelectionUnsafe(SafetySelection):
|
||||
sequencing: Sequencing
|
||||
|
||||
def __init__(self, sequencing: Sequencing = Sequencing.PREFER_ORDERED):
|
||||
assert isinstance(sequencing, Sequencing)
|
||||
self.sequencing = sequencing
|
||||
|
||||
@property
|
||||
def kind(self):
|
||||
return SafetySelectionKind.UNSAFE
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, j: dict) -> Self:
|
||||
if "Unsafe" in j:
|
||||
return cls(Sequencing(j["Unsafe"]))
|
||||
raise Exception("Invalid SafetySelectionUnsafe")
|
||||
|
||||
def to_json(self) -> dict:
|
||||
return {"Unsafe": self.sequencing}
|
||||
|
||||
@final
|
||||
class SafetySelectionSafe(SafetySelection):
|
||||
safety_spec: SafetySpec
|
||||
|
||||
def __init__(self, safety_spec: SafetySpec):
|
||||
assert isinstance(safety_spec, SafetySpec)
|
||||
self.safety_spec = safety_spec
|
||||
|
||||
@property
|
||||
def kind(self):
|
||||
return SafetySelectionKind.SAFE
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, j: dict) -> Self:
|
||||
if "Safe" in j:
|
||||
return cls(SafetySpec.from_json(j["Safe"]))
|
||||
raise Exception("Invalid SafetySelectionUnsafe")
|
||||
|
||||
def to_json(self) -> dict:
|
||||
return {"Safe": self.safety_spec.to_json()}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue