Browse Source

Merge pull request #8139 from SomberNight/202301_locale_decimal_point

locale amounts: consistently use "." as dec point, and " " as thou sep
master
ThomasV 3 years ago committed by GitHub
parent
commit
7d52021d6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      electrum/commands.py
  2. 19
      electrum/exchange_rate.py
  3. 4
      electrum/gui/qml/qefx.py
  4. 18
      electrum/gui/qt/amountedit.py
  5. 2
      electrum/gui/qt/main_window.py
  6. 8
      electrum/tests/test_wallet.py
  7. 12
      electrum/util.py
  8. 4
      electrum/wallet.py

4
electrum/commands.py

@ -1339,8 +1339,8 @@ class Commands:
except InvalidOperation:
raise Exception("from_amount is not a number")
return {
"from_amount": self.daemon.fx.ccy_amount_str(from_amount, False, from_ccy),
"to_amount": self.daemon.fx.ccy_amount_str(to_amount, False, to_ccy),
"from_amount": self.daemon.fx.ccy_amount_str(from_amount, add_thousands_sep=False, ccy=from_ccy),
"to_amount": self.daemon.fx.ccy_amount_str(to_amount, add_thousands_sep=False, ccy=to_ccy),
"from_ccy": from_ccy,
"to_ccy": to_ccy,
"source": self.daemon.fx.exchange.name(),

19
electrum/exchange_rate.py

@ -556,17 +556,24 @@ class FxThread(ThreadJob, EventListener):
return d.get(ccy, [])
@staticmethod
def remove_thousands_separator(text):
return text.replace(',', '') # FIXME use THOUSAND_SEPARATOR in util
def remove_thousands_separator(text: str) -> str:
return text.replace(util.THOUSANDS_SEP, "")
def ccy_amount_str(self, amount, commas, ccy=None):
def ccy_amount_str(self, amount, *, add_thousands_sep: bool = False, ccy=None) -> str:
prec = CCY_PRECISIONS.get(self.ccy if ccy is None else ccy, 2)
fmt_str = "{:%s.%df}" % ("," if commas else "", max(0, prec)) # FIXME use util.THOUSAND_SEPARATOR and util.DECIMAL_POINT
fmt_str = "{:%s.%df}" % ("," if add_thousands_sep else "", max(0, prec))
try:
rounded_amount = round(amount, prec)
except decimal.InvalidOperation:
rounded_amount = amount
return fmt_str.format(rounded_amount)
text = fmt_str.format(rounded_amount)
# replace "," -> THOUSANDS_SEP
# replace "." -> DECIMAL_POINT
dp_loc = text.find(".")
text = text.replace(",", util.THOUSANDS_SEP)
if dp_loc == -1:
return text
return text[:dp_loc] + util.DECIMAL_POINT + text[dp_loc+1:]
async def run(self):
while True:
@ -683,7 +690,7 @@ class FxThread(ThreadJob, EventListener):
def format_fiat(self, value: Decimal) -> str:
if value.is_nan():
return _("No data")
return "%s" % (self.ccy_amount_str(value, True))
return self.ccy_amount_str(value, add_thousands_sep=True)
def history_rate(self, d_t: Optional[datetime]) -> Decimal:
if d_t is None:

4
electrum/gui/qml/qefx.py

@ -108,7 +108,7 @@ class QEFX(QObject, QtEventListener):
except:
return ''
if plain:
return self.fx.ccy_amount_str(self.fx.fiat_value(satoshis, rate), False)
return self.fx.ccy_amount_str(self.fx.fiat_value(satoshis, rate), add_thousands_sep=False)
else:
return self.fx.value_str(satoshis, rate)
@ -133,7 +133,7 @@ class QEFX(QObject, QtEventListener):
return ''
dt = datetime.fromtimestamp(int(td))
if plain:
return self.fx.ccy_amount_str(self.fx.historical_value(satoshis, dt), False)
return self.fx.ccy_amount_str(self.fx.historical_value(satoshis, dt), add_thousands_sep=False)
else:
return self.fx.historical_value_str(satoshis, dt)

18
electrum/gui/qt/amountedit.py

@ -10,7 +10,7 @@ from PyQt5.QtWidgets import (QLineEdit, QStyle, QStyleOptionFrame, QSizePolicy)
from .util import char_width_in_lineedit, ColorScheme
from electrum.util import (format_satoshis_plain, decimal_point_to_base_unit_name,
FEERATE_PRECISION, quantize_feerate)
FEERATE_PRECISION, quantize_feerate, DECIMAL_POINT)
from electrum.bitcoin import COIN, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
@ -66,13 +66,13 @@ class AmountEdit(SizedFreezableLineEdit):
return
pos = self.cursorPosition()
chars = '0123456789'
if not self.is_int: chars +='.'
if not self.is_int: chars += DECIMAL_POINT
s = ''.join([i for i in text if i in chars])
if not self.is_int:
if '.' in s:
p = s.find('.')
s = s.replace('.','')
s = s[:p] + '.' + s[p:p+self.max_precision()]
if DECIMAL_POINT in s:
p = s.find(DECIMAL_POINT)
s = s.replace(DECIMAL_POINT, '')
s = s[:p] + DECIMAL_POINT + s[p:p+self.max_precision()]
if self.max_amount:
if (amt := self._get_amount_from_text(s)) and amt >= self.max_amount:
s = self._get_text_from_amount(self.max_amount)
@ -95,6 +95,7 @@ class AmountEdit(SizedFreezableLineEdit):
def _get_amount_from_text(self, text: str) -> Union[None, Decimal, int]:
try:
text = text.replace(DECIMAL_POINT, '.')
return (int if self.is_int else Decimal)(text)
except:
return None
@ -127,6 +128,7 @@ class BTCAmountEdit(AmountEdit):
def _get_amount_from_text(self, text):
# returns amt in satoshis
try:
text = text.replace(DECIMAL_POINT, '.')
x = Decimal(text)
except:
return None
@ -141,7 +143,9 @@ class BTCAmountEdit(AmountEdit):
return Decimal(amount) if not self.is_int else int(amount)
def _get_text_from_amount(self, amount_sat):
return format_satoshis_plain(amount_sat, decimal_point=self.decimal_point())
text = format_satoshis_plain(amount_sat, decimal_point=self.decimal_point())
text = text.replace('.', DECIMAL_POINT)
return text
def setAmount(self, amount_sat):
if amount_sat is None:

2
electrum/gui/qt/main_window.py

@ -926,7 +926,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
else:
fiat_e.follows = True
fiat_e.setText(self.fx.ccy_amount_str(
amount * Decimal(rate) / COIN, False))
amount * Decimal(rate) / COIN, add_thousands_sep=False))
fiat_e.setStyleSheet(ColorScheme.BLUE.as_stylesheet())
fiat_e.follows = False

8
electrum/tests/test_wallet.py

@ -133,19 +133,19 @@ class TestFiat(ElectrumTestCase):
self.fx = FakeFxThread(FakeExchange(Decimal('1000.001')))
default_fiat = Abstract_Wallet.default_fiat_value(self.wallet, txid, self.fx, self.value_sat)
self.assertEqual(Decimal('1000.001'), default_fiat)
self.assertEqual('1,000.00', self.fx.ccy_amount_str(default_fiat, commas=True))
self.assertEqual('1 000.00', self.fx.ccy_amount_str(default_fiat, add_thousands_sep=True))
def test_save_fiat_and_reset(self):
self.assertEqual(False, Abstract_Wallet.set_fiat_value(self.wallet, txid, ccy, '1000.01', self.fx, self.value_sat))
saved = self.fiat_value[ccy][txid]
self.assertEqual('1,000.01', self.fx.ccy_amount_str(Decimal(saved), commas=True))
self.assertEqual('1 000.01', self.fx.ccy_amount_str(Decimal(saved), add_thousands_sep=True))
self.assertEqual(True, Abstract_Wallet.set_fiat_value(self.wallet, txid, ccy, '', self.fx, self.value_sat))
self.assertNotIn(txid, self.fiat_value[ccy])
# even though we are not setting it to the exact fiat value according to the exchange rate, precision is truncated away
self.assertEqual(True, Abstract_Wallet.set_fiat_value(self.wallet, txid, ccy, '1,000.002', self.fx, self.value_sat))
self.assertEqual(True, Abstract_Wallet.set_fiat_value(self.wallet, txid, ccy, '1 000.002', self.fx, self.value_sat))
def test_too_high_precision_value_resets_with_no_saved_value(self):
self.assertEqual(True, Abstract_Wallet.set_fiat_value(self.wallet, txid, ccy, '1,000.001', self.fx, self.value_sat))
self.assertEqual(True, Abstract_Wallet.set_fiat_value(self.wallet, txid, ccy, '1 000.001', self.fx, self.value_sat))
def test_empty_resets(self):
self.assertEqual(True, Abstract_Wallet.set_fiat_value(self.wallet, txid, ccy, '', self.fx, self.value_sat))

12
electrum/util.py

@ -33,7 +33,7 @@ import urllib
import threading
import hmac
import stat
from locale import localeconv
import locale
import asyncio
import urllib.request, urllib.parse, urllib.error
import builtins
@ -698,7 +698,11 @@ def format_satoshis_plain(
# We enforce that we have at least that available.
assert decimal.getcontext().prec >= 28, f"PyDecimal precision too low: {decimal.getcontext().prec}"
DECIMAL_POINT = localeconv()['decimal_point'] # type: str
# DECIMAL_POINT = locale.localeconv()['decimal_point'] # type: str
DECIMAL_POINT = "."
THOUSANDS_SEP = " "
assert len(DECIMAL_POINT) == 1, f"DECIMAL_POINT has unexpected len. {DECIMAL_POINT!r}"
assert len(THOUSANDS_SEP) == 1, f"THOUSANDS_SEP has unexpected len. {THOUSANDS_SEP!r}"
def format_satoshis(
@ -737,9 +741,9 @@ def format_satoshis(
sign = integer_part[0] if integer_part[0] in ("+", "-") else ""
if sign == "-":
integer_part = integer_part[1:]
integer_part = "{:,}".format(int(integer_part)).replace(',', " ")
integer_part = "{:,}".format(int(integer_part)).replace(',', THOUSANDS_SEP)
integer_part = sign + integer_part
fract_part = " ".join(fract_part[i:i+3] for i in range(0, len(fract_part), 3))
fract_part = THOUSANDS_SEP.join(fract_part[i:i+3] for i in range(0, len(fract_part), 3))
result = integer_part + DECIMAL_POINT + fract_part
# add leading/trailing whitespaces so that numbers can be aligned in a column
if whitespaces:

4
electrum/wallet.py

@ -612,13 +612,13 @@ class Abstract_Wallet(ABC, Logger, EventListener):
# and not util, also have fx remove it
text = fx.remove_thousands_separator(text)
def_fiat = self.default_fiat_value(txid, fx, value_sat)
formatted = fx.ccy_amount_str(def_fiat, commas=False)
formatted = fx.ccy_amount_str(def_fiat, add_thousands_sep=False)
def_fiat_rounded = Decimal(formatted)
reset = not text
if not reset:
try:
text_dec = Decimal(text)
text_dec_rounded = Decimal(fx.ccy_amount_str(text_dec, commas=False))
text_dec_rounded = Decimal(fx.ccy_amount_str(text_dec, add_thousands_sep=False))
reset = text_dec_rounded == def_fiat_rounded
except:
# garbage. not resetting, but not saving either

Loading…
Cancel
Save