Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

money

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

money

Python Money Class

  • 1.3.0
  • PyPI
  • Socket score

Maintainers
1

============ Python Money

Money class with optional CLDR-backed locale-aware formatting and an extensible currency exchange solution.

.. RADAR: version

This is version 1.3.0.

: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 (tested in 2.7.11, 3.4.4, 3.5.1), but there are important Differences between Python versions_. All code examples use Python 3.x.

Contents

.. contents:: :local: :backlinks: none

Installation

Install the latest release with:

::

pip install money

For locale-aware formatting, also install the latest version of Babel <https://pypi.python.org/pypi/Babel>_ (2.2 or higher 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.99

More 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 <https://docs.python.org/3/tutorial/floatingpoint.html>_.

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 2.2 or higher 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 <http://babel.pocoo.org/en/latest/api/numbers.html#babel.numbers.format_currency>. To learn more about the formatting pattern syntax check out Unicode TR35 <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 <https://docs.python.org/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')

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 <https://docs.python.org/2/library/stdtypes.html#comparisons>`_.
  - 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 <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.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.

FAQs


Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc