1

I have the following code which compares the input value that comes as a string with the one the function constracts. Basically checks the if the number the user enters is correct. The function works but only for 2 decimal points but the user is allowed to enter 2 to 4 decimal digits... How can I construct the right value?

def check_currency(self, amount):
        amount = amount.replace("+", "")
        amount = amount.replace("-", "")
        sInput = amount.replace(",", ".")
        locale.setlocale(locale.LC_ALL, self.LANG)
        result = locale.currency(float(sInput), symbol=False, grouping=True)
        if (amount == result):
            return True
        else:
            return False

PS: The self.LANG get the respective value based on the system that the code runs on.

  • What do you mean, exactly? Does it round the number to 2 decimal places, while you want, e.g. fractions of cents? Or does it not work properly for currencies / locales that have more than two decimal places? – tobias_k Aug 11 '15 at 20:15
  • well i mean that when someone submit the string "2.589" instead of getting the locale for german for example which is "2,589" the function returns "2,59".. What am i missing ? –  Aug 11 '15 at 21:09
  • The parenthesis around the `if` condition are unnecessary. The whole `if`/`else` statement is. Just `return amount == result` would do the same. – BlackJack Aug 11 '15 at 23:18
  • yeah the parenthesis are unnecessary. But the whole if/ else statement is there because the function actually returns something not just true/false. Thanx –  Aug 12 '15 at 06:48
  • As a side note: never use `float`s for money as they are inherently imprecise! If integers can't be used, use `Decimal`. If floats are the only option (e.g. to pass to the system's `strfmon()`), only make them when needed (a `Decimal` will be auto-converted on passing it) and only use them for display, not calculations. – ivan_pozdeev Aug 12 '15 at 09:25
  • @ivan_pozdeev That was really helpfull thank you !! –  Aug 12 '15 at 10:15

2 Answers2

3

You aren't missing anything. locale.currency formats numbers according to the values in the dict returned by locale.localeconv() and that's what it's supposed to do. Naturally, 'frac_digits' is typically 2 since... well... you know.


Regarding what to do:

First, you can check 'int_frac_digits' in localeconv() - maybe it's good enough for you.

If not, since locale.currency is located in a Python source module, you can rig... I mean, override its logic. Looking at the source, the simplest method appears to be to replace locale.localeconv() with a wrapper.

Be wary though since such a change will be global. If you don't want it to affect other code using locale, alter local copies of the entities (or the entire module) or make a changed entity e.g. require an additional parameter to behave differently.


On a conceptual note: locale is actually correct - for its purpose - in not allowing to alter the representation. Its task is to format information of a few types according to the local convention - so whatever culture you user pertains to, they will see information in the way they are used to. If you alter the format in any way - that wouldn't any longer be "the local convention"! In fact, by requiring 3 decimal digits, you are already making assumptions about the local convention - which may not stand. E.g. in a decent share of currencies, even small sums can numerically be in the thousands.

Community
  • 1
  • 1
ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
  • Here is the deal.. I basically have to recheck some currencies based on the language. However these currencies can be represented with 2,3 and 4 decimal numbers. So my though in order to do that was to have a function that gets the number (as a string) and recreates it. if both at the end (recreation value and input) are the same then the number is formatted correctly... Do you have a better idea to suggest me ? –  Aug 12 '15 at 06:59
  • 1
    @OneWingedAng3l Why "check" if a number is formatted correctly? Why not format it correctly right away? It appears to me that you need a (custom) version of `locale.currency` that ignores `'frac_digits'` and prints however many fractional digits there are. – ivan_pozdeev Aug 12 '15 at 09:44
  • i am doing an automation test and need to recheck the whole page for everything. So the currencies are already displayed there. The numbers can have from 2 to 4 decimal values and some times these are zeros which python "hides" but i need them. However Thank you ! –  Aug 12 '15 at 10:27
  • 1
    If you're generating any representation that your program produces yourself as well, you're basically duplicating the functionality that you're testing. (And why are you using `locale` then? How do you know its representation will always match your program's?) The only way from this magic circle that I know of is to instead replace it with a very simple, deterministic functionality (the simpler the better, thus ideally a static set of pre-calculated results) and only test cases covered by this replacement. – ivan_pozdeev Aug 12 '15 at 13:12
1
def check_currency(self, amount):
        amount = amount.replace("+", "")
        amount = amount.replace("-", "")
        sInput = amount.replace(",", ".")
        length = len(amount)
        decimals = len(sInput[sInput.find(".")+1:])
        locale.setlocale(locale.LC_ALL, self.LANG)
        if not sInput:
            return  "Empty Value"   
        if decimals<=2:
                digit = decimals
                result = locale.format('%%.%if' % digit, abs(Decimal(sInput)), grouping=True, monetary=True)
        if decimals>2:
                sInput = amount.replace(".", "")
                digit = 0
                result = locale.format('%%.%if' % digit, abs(Decimal(sInput)), grouping=True, monetary=True)
        if (amount == result):
            return True
        else:
            return False

I changed the function as you see above and it works perfect !! You can check the code above for anyone who might need it :)

Thank you again