From 6a2806c2c5143beeb15603f8b6564042bf6f0a0e Mon Sep 17 00:00:00 2001 From: SomberNight Date: Thu, 7 Sep 2023 15:30:46 +0000 Subject: [PATCH] simple_config: implement complex default values Besides a literal value, the default can now also be a callable, which gets called with the config and evaluated as needed, lazily. This potentially allows e.g. the default value of one configvar to depend on the current value of another configvar. --- electrum/simple_config.py | 41 ++++++++++++++++++---------- electrum/submarine_swaps.py | 2 +- electrum/tests/test_simple_config.py | 21 ++++++++++++-- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/electrum/simple_config.py b/electrum/simple_config.py index b8e30eda1..38d70c2a7 100644 --- a/electrum/simple_config.py +++ b/electrum/simple_config.py @@ -4,7 +4,7 @@ import time import os import stat from decimal import Decimal -from typing import Union, Optional, Dict, Sequence, Tuple, Any, Set +from typing import Union, Optional, Dict, Sequence, Tuple, Any, Set, Callable from numbers import Real from functools import cached_property @@ -54,23 +54,34 @@ FINAL_CONFIG_VERSION = 3 class ConfigVar(property): - def __init__(self, key: str, *, default, type_=None): + def __init__( + self, + key: str, + *, + default: Union[Any, Callable[['SimpleConfig'], Any]], # typically a literal, but can also be a callable + type_=None, + ): self._key = key self._default = default self._type = type_ property.__init__(self, self._get_config_value, self._set_config_value) def _get_config_value(self, config: 'SimpleConfig'): - value = config.get(self._key, default=self._default) - if self._type is not None and value != self._default: - assert value is not None, f"got None for key={self._key!r}" - try: - value = self._type(value) - except Exception as e: - raise ValueError( - f"ConfigVar.get type-check and auto-conversion failed. " - f"key={self._key!r}. type={self._type}. value={value!r}") from e - return value + with config.lock: + if config.is_set(self._key): + value = config.get(self._key) + if self._type is not None: + assert value is not None, f"got None for key={self._key!r}" + try: + value = self._type(value) + except Exception as e: + raise ValueError( + f"ConfigVar.get type-check and auto-conversion failed. " + f"key={self._key!r}. type={self._type}. value={value!r}") from e + else: + d = self._default + value = d(config) if callable(d) else d + return value def _set_config_value(self, config: 'SimpleConfig', value, *, save=True): if self._type is not None and value is not None: @@ -817,14 +828,14 @@ class SimpleConfig(Logger): f"Either use config.cv.{name}.set() or assign to config.{name} instead.") return CVLookupHelper() - def get_swapserver_url(self): + def _default_swapserver_url(self) -> str: if constants.net == constants.BitcoinMainnet: default = 'https://swaps.electrum.org/api' elif constants.net == constants.BitcoinTestnet: default = 'https://swaps.electrum.org/testnet' else: default = 'http://localhost:5455' - return self.SWAPSERVER_URL or default + return default # config variables -----> NETWORK_AUTO_CONNECT = ConfigVar('auto_connect', default=True, type_=bool) @@ -940,7 +951,7 @@ class SimpleConfig(Logger): CONFIG_FORGET_CHANGES = ConfigVar('forget_config', default=False, type_=bool) # submarine swap server - SWAPSERVER_URL = ConfigVar('swapserver_url', default='', type_=str) + SWAPSERVER_URL = ConfigVar('swapserver_url', default=_default_swapserver_url, type_=str) SWAPSERVER_PORT = ConfigVar('swapserver_port', default=5455, type_=int) TEST_SWAPSERVER_REFUND = ConfigVar('test_swapserver_refund', default=False, type_=bool) diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py index 34f2d76c4..bc97c9657 100644 --- a/electrum/submarine_swaps.py +++ b/electrum/submarine_swaps.py @@ -206,7 +206,7 @@ class SwapManager(Logger): if swap.prepay_hash is not None: self.prepayments[swap.prepay_hash] = bytes.fromhex(k) # api url - self.api_url = wallet.config.get_swapserver_url() + self.api_url = wallet.config.SWAPSERVER_URL # init default min & max self.init_min_max_values() diff --git a/electrum/tests/test_simple_config.py b/electrum/tests/test_simple_config.py index 3204328a9..0136a2dcf 100644 --- a/electrum/tests/test_simple_config.py +++ b/electrum/tests/test_simple_config.py @@ -3,9 +3,10 @@ import sys import os import tempfile import shutil - from io import StringIO -from electrum.simple_config import (SimpleConfig, read_user_config) + +from electrum.simple_config import SimpleConfig, read_user_config +from electrum import constants from . import ElectrumTestCase @@ -147,6 +148,22 @@ class Test_SimpleConfig(ElectrumTestCase): config.NETWORK_MAX_INCOMING_MSG_SIZE = None self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.NETWORK_MAX_INCOMING_MSG_SIZE) + def test_configvars_get_default_value_complex_fn(self): + config = SimpleConfig(self.options) + self.assertEqual("https://swaps.electrum.org/api", config.SWAPSERVER_URL) + + config.SWAPSERVER_URL = "http://localhost:9999" + self.assertEqual("http://localhost:9999", config.SWAPSERVER_URL) + + config.SWAPSERVER_URL = None + self.assertEqual("https://swaps.electrum.org/api", config.SWAPSERVER_URL) + + constants.set_testnet() + try: + self.assertEqual("https://swaps.electrum.org/testnet", config.SWAPSERVER_URL) + finally: + constants.set_mainnet() + def test_configvars_is_set(self): config = SimpleConfig(self.options) self.assertEqual(MAX_MSG_SIZE_DEFAULT, config.NETWORK_MAX_INCOMING_MSG_SIZE)