============ Python Money ============ Money class with optional CLDR-backed locale-aware formatting and an extensible currency exchange solution. All code examples use Python 3.x. **Contents** .. contents:: :local: :backlinks: none Installation ============ This package is compatible with Python 2.7, 3.3, 3.4, but there are important `Differences between Python versions`_. :: pip install money For locale-aware formatting, also install `Babel <https://pypi.python.org/pypi/Babel>`_: :: pip install babel Basic usage =========== .. code:: python >>> from money import Money >>> m = Money(amount='2.22', currency='EUR') >>> m EUR 2.22 *amount* can be any valid value for decimal.Decimal(value) and *currency* should be a three-letter currency code. You can perform most arithmetic operations between money objects and integers or decimals. .. code:: python >>> from money import Money >>> m = Money('2.22', 'EUR') >>> m / 2 EUR 1.11 >>> m + Money('7.77', 'EUR') EUR 9.99 Arithmetic operations with floats are not directly supported. If you need to operate with floats, you must first convert the float to a Decimal, or the Money object to a float (i.e. float(m)). Please be aware of the `issues and limitations of floating point arithmetics <https://docs.python.org/3/tutorial/floatingpoint.html>`_. Formatting ========== Money objects are printed by default with en_US formatting and the currency code. .. code:: python >>> m = Money('1234.567', 'EUR') >>> str(m) 'EUR 1,234.57' Use ``format(locale=DEFAULT_LC_NUMERIC, pattern=None)`` for locale-aware formatting with currency expansion. ``format()`` emulates ``babel.numbers.format_currency()``, and **requires Babel** to be installed: .. code:: python >>> m = Money('1234.567', 'USD') >>> m.format('en_US') '$1,234.57' >>> m.format('es_ES') '1.234,57\xa0US$' The character ``\xa0`` is an unicode non-breaking space (chicken-good). If no locale is passed, Babel will use your system's locale. You can also provide a specific pattern to format(): .. code:: python >>> m = Money('-1234.567', 'USD') >>> # Regular US format: >>> m.format('en_US', '¤#,##0.00') '-$1,234.57' >>> # Custom negative format: >>> m.format('en_US', '¤#,##0.00;<¤#,##0.00>') '<$1,234.57>' >>> # Round, Spanish format, full currency name: >>> m.format('es_ES', '0 ¤¤¤') '-1235 dólares estadounidenses' `Learn more about the formatting syntax: <http://www.unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns>`_. Currency exchange ================= Currency exchange works by "installing" a **backend** class that implements the abstract base class (`abc <http://docs.python.org/3.3/library/abc.html>`_) ``money.exchange.BackendBase``. Its API is exposed through ``money.xrates``, along with setup functions ``xrates.install(pythonpath)``, ``xrates.uninstall()``, and ``xrates.backend_name``. A simple proof-of-concept backend ``money.exchange.SimpleBackend`` is included: .. code:: python from decimal import Decimal from money import Money, xrates xrates.install('money.exchange.SimpleBackend') xrates.base = 'USD' xrates.setrate('AAA', Decimal('2')) xrates.setrate('BBB', Decimal('8')) a = Money(1, 'AAA') b = Money(1, 'BBB') assert a.to('BBB') == Money('4', 'BBB') assert b.to('AAA') == Money('0.25', 'AAA') assert a + b.to('AAA') == Money('1.25', 'AAA') You can use a subclass of Money, **XMoney** if you prefer automatic conversion between different currencies on binary operations. The currency of the leftmost object has priority. .. code:: python from money import XMoney # Register backend and rates as above... a = XMoney(1, 'AAA') b = XMoney(1, 'BBB') assert a + b == XMoney('1.25', 'AAA') .. _python-differences: Differences between Python versions =================================== .. list-table:: :header-rows: 1 :stub-columns: 1 * - Expression - Python 2.x - Python 3.x * - ``round(Money('2.5', 'EUR'))`` - Returns ``3.0``, a **float** rounded amount **away from zero**. - Returns ``EUR 2``, a **Money object** with rounded amount to the **nearest even**. Design decisions ================ There are several design decisions in *money* that differ from currently available money class implementations: Localization ------------ Do not keep any kind of locale conventions database inside this package. Locale conventions are extensive and change over time; keeping track of them is a project of its own. There is already such a project and database (the Unicode Common Locale Data Repository), and an excellent python API for it: `Babel <https://pypi.python.org/pypi/Babel>`_. Currency -------- There is no need for a currency class. A currency is fully identified by its ISO 4217 code, and localization or exchange rates data are expected to be centralized as databases/services because of their changing nature. Also: + **Modulo operator (%)**: do not override to mean "percentage". + **Numeric type**: you **can** mix numbers and money in binary operations, and objects evaluate to False if their amount is zero. + **Global default currency**: subclassing is a safer solution. Contributions ============= Contributions are welcome. You can use the `regular github mechanisms <https://help.github.com/>`_. To be forward-compatible, and given the small size of the package, Python 2.7 is supported in a different source "branch" at ``src-py2``. To test your changes you will need `tox <https://pypi.python.org/pypi/tox>`_ and python 2.7, 3.3, and 3.4. Simply cd to the package root (by setup.py) and run ``tox``. License ======= money is released under the **MIT license**, which can be found in the file ``LICENSE``.