Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/carlospalol/money
Python money class with optional CLDR-backed locale-aware formatting and an extensible currency exchange solution.
https://github.com/carlospalol/money
Last synced: about 1 month ago
JSON representation
Python money class with optional CLDR-backed locale-aware formatting and an extensible currency exchange solution.
- Host: GitHub
- URL: https://github.com/carlospalol/money
- Owner: carlospalol
- License: mit
- Created: 2013-11-16T06:03:45.000Z (about 11 years ago)
- Default Branch: master
- Last Pushed: 2023-04-08T19:03:59.000Z (over 1 year ago)
- Last Synced: 2024-10-01T11:19:20.950Z (2 months ago)
- Language: Python
- Homepage:
- Size: 136 KB
- Stars: 230
- Watchers: 11
- Forks: 34
- Open Issues: 11
-
Metadata Files:
- Readme: README.rst
- Changelog: CHANGELOG.rst
- License: LICENSE
Awesome Lists containing this project
- awesome-fintech - money - Python money class with optional CLDR-backed locale-aware formatting and an extensible currency exchange solution. (Libraries / Python)
- awesome-python-resources - GitHub - 30% open · ⏱️ 04.09.2016): (电子商务)
- starred-awesome - money - Python money class with optional CLDR-backed locale-aware formatting and an extensible currency exchange solution. (Python)
README
============
Python Money
============Money class with optional CLDR-backed locale-aware formatting and an extensible currency exchange solution.
.. RADAR: version
**This is version 1.4.0-dev**.
:Development: https://github.com/carlospalol/money
:Latest release: https://pypi.python.org/pypi/money/This package is compatible with Python 2.7, 3.4, 3.5, but there are important `Differences between Python versions`_. All code examples use Python 3.5.
**Contents**
.. contents::
:local:
:backlinks: noneInstallation
============Install the latest release with:
::
pip install money
For locale-aware formatting, also install the latest version of `Babel `_ (2.2 or 2.3 required):
::
pip install babel
Usage
=====.. code:: python
>>> from money import Money
>>> m = Money(amount='2.22', currency='EUR')
>>> m
EUR 2.22*amount* can be any valid value in ``decimal.Decimal(value)`` and *currency* should be a three-letter currency code. Money objects are immutable by convention and hashable. Once created, you can use read-only properties ``amount`` (decimal.Decimal) and ``currency`` (str) to access its internal components:
.. code:: python
>>> m = Money(2, 'USD')
>>> m.amount
Decimal('2')
>>> m.currency
'USD'Money emulates a numeric type and you can apply most arithmetic and comparison operators between money objects, as well as addition, subtraction, and division with integers (int) and decimal numbers (decimal.Decimal):
.. code:: python
>>> m = Money('2.22', 'EUR')
>>> m / 2
EUR 1.11
>>> m + Money('7.77', 'EUR')
EUR 9.99More formally, with *AAA* and *BBB* being different currencies:
+-----------+---------------+-----------+-----------+-----------------+
| | Operator | Money AAA | Money BBB | int, Decimal |
+===========+===============+===========+===========+=================+
| **Money | ``+``, ``-`` | Money | N/A | Money |
+ AAA** +---------------+-----------+-----------+-----------------+
| | ``*`` | N/A | N/A | Money |
+ +---------------+-----------+-----------+-----------------+
| | ``/``, ``//`` | Decimal | N/A | Money |
+ +---------------+-----------+-----------+-----------------+
| | ``>``, ``>=`` | Compares | N/A | N/A |
| | ``<``, ``<=`` | amount. | | |
+ +---------------+ +-----------+-----------------+
| | ``==`` | | False | False |
| | | | | |
+-----------+---------------+-----------+-----------+-----------------+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 `_.
Currency presets
----------------If you use fixed currencies in your code, you may find convenient to create currency-preset Money subclasses:
.. code:: python
class EUR(Money):
def __init__(self, amount='0'):
super().__init__(amount=amount, currency='EUR')
price = EUR('9.99')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=LC_NUMERIC, pattern=None, currency_digits=True, format_type='standard')`` for locale-aware formatting with currency expansion. ``format()`` relies on ``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\xa0$'The character ``\xa0`` is an unicode non-breaking space. 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>'
>>> # Spanish format, full currency name:
>>> m.format('es_ES', '#,##0.00 ¤¤¤')
'-1.234,57 dólares estadounidenses'
>>> # Same as above, but rounding (overriding currency natural format):
>>> m.format('es_ES', '#0 ¤¤¤', currency_digits=False)
'-1235 dólares estadounidenses'For more details on formatting see `Babel docs on currency formatting `_. To learn more about the formatting pattern syntax check out `Unicode TR35 `_.
Currency exchange
=================Currency exchange works by "installing" a **backend** class that implements the abstract base class (`abc `_) ``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, xratesxrates.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')XMoney
======You can use ``money.XMoney`` (a subclass of Money), for automatic currency conversion while adding, subtracting, and dividing money objects (+, +=, -, -=, /, //). This is useful when aggregating lots of money objects with heterogeneous currencies. 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 sum([a, b]) == XMoney('1.25', 'AAA')Exceptions
==========Found in ``money.exceptions``.
``MoneyException(Exception)``
Base class for all exceptions.``CurrencyMismatch(MoneyException, ValueError)``
Thrown when mixing different currencies, e.g. ``Money(2, 'EUR') + Money(2, 'USD')``. Money objects must be converted first to the same currency, or XMoney could be used for automatic conversion.``InvalidOperandType(MoneyException, TypeError)``
Thrown when attempting invalid operations, e.g. multiplication between money objects.``ExchangeError(MoneyException)``
Base class for exchange exceptions.``ExchangeBackendNotInstalled(ExchangeError)``
Thrown if a conversion is attempted, but there is no backend available.``ExchangeRateNotFound(ExchangeError)``
The installed backend failed to provide a suitable exchange rate between the origin and target currencies.Hierarchy
---------* ``MoneyException``
* ``CurrencyMismatch``
* ``InvalidOperandType``
* ``ExchangeError``
* ``ExchangeBackendNotInstalled``
* ``ExchangeRateNotFound``.. _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**.
* - ``Money('0', 'EUR').amount < '0'``
- Returns ``True``. This is the weird but expected behaviour in Python 2.x when comparing Decimal objects with non-numerical objects (Note the '0' is a string). `See note in docs `_.
- TypeError: unorderable types: decimal.Decimal() > str()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 `_.
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 `_.
To test your changes you will need `tox `_ and python 2.7, 3.4, and 3.5. 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``.