Source code for kiwi.currency

#
# Kiwi: a Framework and Enhanced Widgets for Python
#
# Copyright (C) 2005-2006 Async Open Source
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
# USA
#
# Author(s): Johan Dahlin <jdahlin@async.com.br>
#

"""Currency and datatype converter"""

import gettext

from kiwi.datatypes import HAVE_DECIMAL, Decimal, InvalidOperation
from kiwi.datatypes import ValidationError, ValueUnset
from kiwi.datatypes import converter, get_localeconv, filter_locale
from kiwi.enums import Alignment

_ = lambda m: gettext.dgettext('kiwi', m)

[docs]class currency(Decimal): """ A datatype representing currency, used together with the list and the framework """ _converter = converter.get_converter(Decimal) def __new__(cls, value): """ Convert value to currency. :param value: value to convert :type value: string or number """ if isinstance(value, str): conv = get_localeconv() currency_symbol = conv.get('currency_symbol') text = value.strip(currency_symbol) # if we cannot convert it using locale information, still try to # create try: text = filter_locale(text, monetary=True) value = currency._converter.from_string(text) except ValidationError: # Decimal does not accept leading and trailing spaces. See # bug 1516613 value = text.strip() if value == ValueUnset: raise InvalidOperation elif HAVE_DECIMAL and isinstance(value, float): print ('Warning: losing precision converting float %r to currency' % value) value = str(value) elif not isinstance(value, (int, long, Decimal)): raise TypeError( "cannot convert %r of type %s to a currency" % ( value, type(value))) return Decimal.__new__(cls, value)
[docs] def format(self, symbol=True, precision=None): value = Decimal(self) conv = get_localeconv() # Grouping (eg thousand separator) of integer part groups = conv.get('mon_grouping', [])[:] groups.reverse() if groups: group = groups.pop() else: group = 3 intparts = [] # We're iterating over every character in the integer part # make sure to remove the negative sign, it'll be added later intpart = str(int(abs(value))) while True: if not intpart: break s = intpart[-group:] intparts.insert(0, s) intpart = intpart[:-group] if not groups: continue last = groups.pop() # if 0 reuse last one, see struct lconv in locale.h if last != 0: group = last # Add the sign, and the list of decmial parts, which now are grouped # properly and can be joined by mon_thousand_sep if value > 0: sign = conv.get('positive_sign', '') elif value < 0: sign = conv.get('negative_sign', '-') else: sign = '' currency = sign + conv.get('mon_thousands_sep', '.').join(intparts) # Only add decimal part if it has one, is this correct? if precision is not None or value % 1 != 0: # Pythons string formatting can't handle %.127f # 127 is the default value from glibc/python if precision: frac_digits = precision else: frac_digits = conv.get('frac_digits', 2) if frac_digits == 127: frac_digits = 2 format = '%%.%sf' % (frac_digits+1) dec_part = (format % value)[-(frac_digits+1):-1] mon_decimal_point = conv.get('mon_decimal_point', '.') currency += mon_decimal_point + dec_part # If requested include currency symbol currency_symbol = conv.get('currency_symbol', '') if currency_symbol and symbol: if value > 0: cs_precedes = conv.get('p_cs_precedes', 1) sep_by_space = conv.get('p_sep_by_space', 1) else: cs_precedes = conv.get('n_cs_precedes', 1) sep_by_space = conv.get('n_sep_by_space', 1) if sep_by_space: space = ' ' else: space = '' if cs_precedes: currency = currency_symbol + space + currency else: currency = currency + space + currency_symbol return currency
def __repr__(self): return '<currency %s>' % self.format()
_DecimalConverter = type(converter.get_converter(Decimal)) class _CurrencyConverter(_DecimalConverter): type = currency name = _('Currency') align = Alignment.RIGHT def __init__(self): self.symbol = True self.precision = 2 def as_string(self, value, format=None, symbol=None, precision=None): if value == ValueUnset: return '' if not isinstance(value, currency): try: value = currency(value) except ValueError: raise ValidationError( _("%s can not be converted to a currency") % value) if symbol is None: symbol = self.symbol if precision is None: precision = self.precision return value.format(symbol, precision) def from_string(self, value): if value == '': return ValueUnset try: return currency(value) except (ValueError, InvalidOperation): raise ValidationError( _("%s can not be converted to a currency") % value) converter.add(_CurrencyConverter)
[docs]def format_price(value, symbol=True, precision=None): """ Formats a price according to the current locales monetary settings :param value: number :param symbol: whether to include the currency symbol """ return currency(value).format(symbol, precision)