glclient.lsps
1from dataclasses import dataclass, is_dataclass, asdict, field 2 3import typing as t 4import json 5import time 6import binascii 7 8import glclient.glclient as native 9 10import logging 11 12logger = logging.getLogger(__name__) 13 14class EnhancedJSONEncoder(json.JSONEncoder): 15 def default(self, o): 16 if is_dataclass(o): 17 return asdict(o) 18 elif isinstance(o, NoParams): 19 return dict() 20 elif isinstance(o, type) and o.__name__ == "NoParams": 21 return dict() 22 return super().default(o) 23 24 25class AsDataClassDescriptor: 26 """Descriptor that allows to initialize a nested dataclass from a nested directory""" 27 28 def __init__(self, *, cls): 29 self._cls = cls 30 31 def __set_name__(self, owner, name): 32 self._name = f"_{name}" 33 34 def __get__(self, obj, type): 35 return getattr(obj, self._name, None) 36 37 def __set__(self, obj, value): 38 if isinstance(value, self._cls): 39 setattr(obj, self._name, value) 40 else: 41 setattr(obj, self._name, self._cls(**value)) 42 43 44def _dump_json_bytes(object: t.Any) -> bytes: 45 json_str: str = json.dumps(object, cls=EnhancedJSONEncoder) 46 json_bytes: bytes = json_str.encode("utf-8") 47 return json_bytes 48 49 50@dataclass 51class ProtocolList: 52 protocols: t.List[int] 53 54 55@dataclass 56class Lsps1Options: 57 minimum_channel_confirmations: t.Optional[int] 58 minimum_onchain_payment_confirmations: t.Optional[int] 59 supports_zero_channel_reserve: t.Optional[bool] 60 min_onchain_payment_size_sat: t.Optional[int] 61 max_channel_expiry_blocks: t.Optional[int] 62 min_initial_client_balance_sat: t.Optional[int] 63 min_initial_lsp_balance_sat: t.Optional[int] 64 max_initial_client_balance_sat: t.Optional[int] 65 min_channel_balance_sat: t.Optional[int] 66 max_channel_balance_sat: t.Optional[int] 67 68 69class NoParams: 70 pass 71 72 73class LspClient: 74 def __init__(self, native: native.LspClient): 75 self._native = native 76 77 def _rpc_call( 78 self, 79 peer_id: str, 80 method_name: str, 81 param_json: bytes, 82 json_rpc_id: t.Optional[str] = None, 83 ) -> bytes: 84 logger.debug("Request lsp to peer %s and method %s", peer_id, method_name) 85 peer_id_bytes = bytes.fromhex(peer_id) 86 if json_rpc_id is None: 87 return self._native.rpc_call(peer_id_bytes, method_name, param_json) 88 else: 89 return self._native.rpc_call_with_json_rpc_id( 90 peer_id_bytes, method_name, param_json, json_rpc_id=json_rpc_id 91 ) 92 93 def list_lsp_servers(self) -> t.List[str]: 94 return self._native.list_lsp_servers() 95 96 def list_protocols(self, peer_id, json_rpc_id: t.Optional[str] = None) -> ProtocolList: 97 json_bytes = _dump_json_bytes(NoParams) 98 result = self._rpc_call( 99 peer_id, "lsps0.list_protocols", json_bytes, json_rpc_id=json_rpc_id 100 ) 101 response_dict = json.loads(result) 102 return ProtocolList(**response_dict)
15class EnhancedJSONEncoder(json.JSONEncoder): 16 def default(self, o): 17 if is_dataclass(o): 18 return asdict(o) 19 elif isinstance(o, NoParams): 20 return dict() 21 elif isinstance(o, type) and o.__name__ == "NoParams": 22 return dict() 23 return super().default(o)
Extensible JSON http://json.org encoder for Python data structures.
Supports the following objects and types by default:
+-------------------+---------------+ | Python | JSON | +===================+===============+ | dict | object | +-------------------+---------------+ | list, tuple | array | +-------------------+---------------+ | str | string | +-------------------+---------------+ | int, float | number | +-------------------+---------------+ | True | true | +-------------------+---------------+ | False | false | +-------------------+---------------+ | None | null | +-------------------+---------------+
To extend this to recognize other objects, subclass and implement a
.default()
method with another method that returns a serializable
object for o
if possible, otherwise it should call the superclass
implementation (to raise TypeError
).
16 def default(self, o): 17 if is_dataclass(o): 18 return asdict(o) 19 elif isinstance(o, NoParams): 20 return dict() 21 elif isinstance(o, type) and o.__name__ == "NoParams": 22 return dict() 23 return super().default(o)
Implement this method in a subclass such that it returns
a serializable object for o
, or calls the base implementation
(to raise a TypeError
).
For example, to support arbitrary iterators, you could implement default like this::
def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# Let the base class default method raise the TypeError
return JSONEncoder.default(self, o)
Inherited Members
- json.encoder.JSONEncoder
- JSONEncoder
- encode
- iterencode
26class AsDataClassDescriptor: 27 """Descriptor that allows to initialize a nested dataclass from a nested directory""" 28 29 def __init__(self, *, cls): 30 self._cls = cls 31 32 def __set_name__(self, owner, name): 33 self._name = f"_{name}" 34 35 def __get__(self, obj, type): 36 return getattr(obj, self._name, None) 37 38 def __set__(self, obj, value): 39 if isinstance(value, self._cls): 40 setattr(obj, self._name, value) 41 else: 42 setattr(obj, self._name, self._cls(**value))
Descriptor that allows to initialize a nested dataclass from a nested directory
56@dataclass 57class Lsps1Options: 58 minimum_channel_confirmations: t.Optional[int] 59 minimum_onchain_payment_confirmations: t.Optional[int] 60 supports_zero_channel_reserve: t.Optional[bool] 61 min_onchain_payment_size_sat: t.Optional[int] 62 max_channel_expiry_blocks: t.Optional[int] 63 min_initial_client_balance_sat: t.Optional[int] 64 min_initial_lsp_balance_sat: t.Optional[int] 65 max_initial_client_balance_sat: t.Optional[int] 66 min_channel_balance_sat: t.Optional[int] 67 max_channel_balance_sat: t.Optional[int]
74class LspClient: 75 def __init__(self, native: native.LspClient): 76 self._native = native 77 78 def _rpc_call( 79 self, 80 peer_id: str, 81 method_name: str, 82 param_json: bytes, 83 json_rpc_id: t.Optional[str] = None, 84 ) -> bytes: 85 logger.debug("Request lsp to peer %s and method %s", peer_id, method_name) 86 peer_id_bytes = bytes.fromhex(peer_id) 87 if json_rpc_id is None: 88 return self._native.rpc_call(peer_id_bytes, method_name, param_json) 89 else: 90 return self._native.rpc_call_with_json_rpc_id( 91 peer_id_bytes, method_name, param_json, json_rpc_id=json_rpc_id 92 ) 93 94 def list_lsp_servers(self) -> t.List[str]: 95 return self._native.list_lsp_servers() 96 97 def list_protocols(self, peer_id, json_rpc_id: t.Optional[str] = None) -> ProtocolList: 98 json_bytes = _dump_json_bytes(NoParams) 99 result = self._rpc_call( 100 peer_id, "lsps0.list_protocols", json_bytes, json_rpc_id=json_rpc_id 101 ) 102 response_dict = json.loads(result) 103 return ProtocolList(**response_dict)
97 def list_protocols(self, peer_id, json_rpc_id: t.Optional[str] = None) -> ProtocolList: 98 json_bytes = _dump_json_bytes(NoParams) 99 result = self._rpc_call( 100 peer_id, "lsps0.list_protocols", json_bytes, json_rpc_id=json_rpc_id 101 ) 102 response_dict = json.loads(result) 103 return ProtocolList(**response_dict)