diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..3e8d5df --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,178 @@ +version: 2.1 + +commands: + tester: + description: "Test iso4217parse given a certain python version." + steps: + - checkout + + - run: + name: Set PATH. + command: | + echo 'export PATH=$HOME/.local/bin:$PATH' >> $BASH_ENV + source $BASH_ENV + + - restore_cache: + key: v1-iso4217parse-{{ .Environment.CIRCLE_STAGE }}-{{ checksum "pyproject.toml" }}-{{ checksum "setup.cfg" }} + + - run: + name: Pre-install + command: | + pip install --user poetry virtualenv + virtualenv venv + + - run: + name: Install + command: | + . venv/bin/activate + poetry install + + - save_cache: + key: v1-iso4217parse-{{ .Environment.CIRCLE_STAGE }}-{{ checksum "pyproject.toml" }}-{{ checksum "setup.cfg" }} + paths: + - ~/app/venv + - ~/app/poetry.lock + + - store_artifacts: + path: ~/app/poetry.lock + destination: poetry.lock + + - run: + name: Style + command: | + . venv/bin/activate + poetry run flake8 + + - run: + name: Test + command: | + . venv/bin/activate + poetry run pytest + + - run: + name: Build sdist + command: poetry build -vvv -f sdist + + - store_artifacts: + path: ~/app/dist + destination: dist + + - store_artifacts: + path: ~/app/cov_html + destination: cov_html + + - run: + name: Coverage + command: | + . venv/bin/activate + poetry run coveralls + +executors: + python: + working_directory: ~/app + parameters: + image: + type: string + default: latest + docker: + - image: << parameters.image >> + +jobs: + test_2_7: + executor: + name: python + image: "circleci/python:2.7.15" + steps: + - tester + test_2_7_pypy: + executor: + name: python + image: "pypy:2-6.0.0" + steps: + - tester + test_3_5: + executor: + name: python + image: "circleci/python:3.5.6" + steps: + - tester + test_3_5_pypy: + executor: + name: python + image: "pypy:3-6.0.0" + steps: + - tester + test_3_6: + executor: + name: python + image: "circleci/python:3.6.7" + steps: + - tester + test_3_7: + executor: + name: python + image: "circleci/python:3.7.1" + steps: + - tester + + deploy_job: + docker: + - image: circleci/python:3.6.7 + description: "Deploy iso4217parse to pypi." + steps: + - checkout + + - run: + name: Set PATH. + command: | + echo 'export PATH=$HOME/.local/bin:$PATH' >> $BASH_ENV + source $BASH_ENV + + - run: + name: Pre-install + command: | + pip install --user poetry + poetry build -vvv -f sdist + poetry publish -vvv -n -u tammoippen -p $PYPI_PASS + +workflows: + version: 2.1 + test_and_deploy: + jobs: + - test_2_7: + filters: + tags: + only: /.*/ + - test_2_7_pypy: + filters: + tags: + only: /.*/ + - test_3_5: + filters: + tags: + only: /.*/ + - test_3_5_pypy: + filters: + tags: + only: /.*/ + - test_3_6: + filters: + tags: + only: /.*/ + - test_3_7: + filters: + tags: + only: /.*/ + - deploy_job: + requires: + - test_2_7 + - test_2_7_pypy + - test_3_5 + - test_3_5_pypy + - test_3_6 + - test_3_7 + filters: + branches: + ignore: /.*/ + tags: + only: /v[0-9]+(\.[0-9]+)*/ diff --git a/.gitignore b/.gitignore index 21c5389..7f41a4e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,9 @@ __pycache__/ .vscode/ dist/ pyproject.lock +poetry.lock +cov_html/ +.pytest_cache/ +.hypothesis/ +.venv/ +venv/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f3c41fc..0000000 --- a/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ -language: python -sudo: false - -notifications: - email: false - -python: - - "2.7" - - "3.5" - - "3.6" - - "3.7-dev" - - "nightly" - - "pypy" - - "pypy3.5" - -env: - global: - secure: fYDKFiKFTuYPX7cq2FiqrrtfysmrMlahXglWwDhDQfUcFG3vkZvmvwVFJCjW8wDgg3OCQR4McBaCWbg2Us9OxM0ZveI2BAbAJMfgzIfLM3Kr5yWoPYQ87wIjsr3To4LrsLWIsPSNaXIONfXKN0MhSYHHE1XMf4D+HBv2NqIuJMh3psP1Tl10H81JNcICm9UoX4HADyf5YPtkWWsK/C7st0rjQeBb9d/XwGKsxIGjVGFnQJgyCPLcIQfakIu7394bkEF4us/AaEjmv54/rR1lGWHSR3fRzv6sNobDBbrjAJCKeL7rKBoICOXit/vG51ut3vEx3w62quL16oCiWagCfDOEl8a5YNl+sWihorZJif83gMhL2oSLtyufFtT9ik59H6y/hZK9Q7r6VkLyeDtT1dt0FtoaXcULhZqt79bYRvedcNHP9rUo90reUiSbuNQMZahapBFfXl2OdVqpB892e3qZvMrX/GGdd0YMuKLiobvlK0QeYbTmkYUT0J5bdzD70PoNDj6h69Rlt4ZlMtzFwnYlCP92BzAOy923R8nzyO9HPrTK0IBQ80tACKW4I7B4+tvdenGDdDyc0D8VjNvFbK4QUelNALDYps7+0GLrFpVkZrZI3ezY8dX9ZArKuZOMxlWRI6yzCuNWFw1RZC5Om6xB1aWUlO1DUuA7wbCWFMA= - -addons: - apt: - packages: - - pandoc - -install: - - pip install poetry - - poetry install --verbose - -script: poetry run pytest - -after_success: - - coveralls - -before_deploy: - - git stash --all - - poetry build -f sdist - -deploy: - provider: script - script: poetry publish -u tammoippen -p $PYPI_PASS - skip_cleanup: true - on: - branch: master - tags: true - python: "3.6" diff --git a/README.md b/README.md index c80dd72..f3f8a43 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -[![Build Status](https://travis-ci.org/tammoippen/iso4217parse.svg?branch=master)](https://travis-ci.org/tammoippen/iso4217parse) +[![CircleCI](https://circleci.com/gh/tammoippen/iso4217parse.svg?style=svg)](https://circleci.com/gh/tammoippen/iso4217parse) [![Coverage Status](https://coveralls.io/repos/github/tammoippen/iso4217parse/badge.svg?branch=master)](https://coveralls.io/github/tammoippen/iso4217parse?branch=master) -[![Tested CPython Versions](https://img.shields.io/badge/cpython-2.7%2C%203.5%2C%203.6%2C%20nightly-brightgreen.svg)](https://img.shields.io/badge/cpython-2.7%2C%203.5%2C%203.6%2C%20nightly-brightgreen.svg) -[![Tested PyPy Versions](https://img.shields.io/badge/pypy-2.7--5.8.0%2C%203.5--5.8.0-brightgreen.svg)](https://img.shields.io/badge/pypy-2.7--5.8.0%2C%203.5--5.8.0-brightgreen.svg) +[![Tested CPython Versions](https://img.shields.io/badge/cpython-2.7%2C%203.5%2C%203.6%2C%203.7-brightgreen.svg)](https://img.shields.io/badge/cpython-2.7%2C%203.5%2C%203.6%2C%203.7-brightgreen.svg) +[![Tested PyPy Versions](https://img.shields.io/badge/pypy-2.7--6.0.0%2C%203.5--6.0.0-brightgreen.svg)](https://img.shields.io/badge/pypy-2.7--6.0.0%2C%203.5--6.0.0-brightgreen.svg) [![PyPi version](https://img.shields.io/pypi/v/iso4217parse.svg)](https://pypi.python.org/pypi/iso4217parse) [![PyPi license](https://img.shields.io/pypi/l/iso4217parse.svg)](https://pypi.python.org/pypi/iso4217parse) @@ -43,27 +43,27 @@ Currency = namedtuple('Currency', [ In [1]: import iso4217parse In [2]: iso4217parse.parse('CHF') -Out[2]: [Currency(alpha3='CHF', code_num=756, name='Swiss franc', +Out[2]: [Currency(alpha3='CHF', code_num=756, name='Swiss franc', symbols=['SFr.', 'fr', 'Fr.', 'F', 'franc', 'francs', 'Franc', 'Francs'], minor=2, countries=['CH', 'LI'])] In [3]: iso4217parse.parse(192) Out[3]: -[Currency(alpha3='CUP', code_num=192, name='Cuban peso', - symbols=['₱', '$', '﹩', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', '$MN', '﹩MN', '$MN'], +[Currency(alpha3='CUP', code_num=192, name='Cuban peso', + symbols=['₱', '$', '﹩', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', '$MN', '﹩MN', '$MN'], minor=2, countries=['CU'])] In [4]: iso4217parse.parse('Price is 5 €') -Out[4]: [Currency(alpha3='EUR', code_num=978, name='Euro', - symbols=['€', 'euro', 'euros'], minor=2, +Out[4]: [Currency(alpha3='EUR', code_num=978, name='Euro', + symbols=['€', 'euro', 'euros'], minor=2, countries=['AD', 'AT', 'AX', 'BE', 'BL', 'CY', 'DE', 'EE', 'ES', 'FI', 'FR', 'GF', 'GP', 'GR', 'IE', 'IT', 'LT', 'LU', 'LV', 'MC', 'ME', 'MF', 'MQ', 'MT', 'NL', 'PM', 'PT', 'RE', 'SI', 'SK', 'SM', 'TF', 'VA', 'XK', 'YT'])] In [5]: iso4217parse.parse('CA﹩15.76') -Out[5]: [Currency(alpha3='CAD', code_num=124, name='Canadian dollar', - symbols=['CA$', 'CA$', '$', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', 'CA﹩', '﹩'], +Out[5]: [Currency(alpha3='CAD', code_num=124, name='Canadian dollar', + symbols=['CA$', 'CA$', '$', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', 'CA﹩', '﹩'], minor=2, countries=['CA'])] In [6]: iso4217parse.parse? @@ -90,7 +90,7 @@ Returns: In [1]: import iso4217parse In [2]: iso4217parse.by_alpha3('CHF') -Out[2]: Currency(alpha3='CHF', code_num=756, name='Swiss franc', +Out[2]: Currency(alpha3='CHF', code_num=756, name='Swiss franc', symbols=['SFr.', 'fr', 'Fr.', 'F', 'franc', 'francs', 'Franc', 'Francs'], minor=2, countries=['CH', 'LI']) @@ -111,7 +111,7 @@ Returns: In [1]: import iso4217parse In [2]: iso4217parse.by_code_num(51) -Out[2]: Currency(alpha3='AMD', code_num=51, name='Armenian dram', +Out[2]: Currency(alpha3='AMD', code_num=51, name='Armenian dram', symbols=['֏', 'դր', 'dram'], minor=2, countries=['AM']) In [3]: iso4217parse.by_code_num? @@ -131,13 +131,13 @@ Returns: In [1]: import iso4217parse In [2]: iso4217parse.country('HK') -Out[2]: -[ - Currency(alpha3='HKD', code_num=344, name='Hong Kong dollar', - symbols=['HK$', 'HK$', '$', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', 'HK﹩', '﹩', '元'], +Out[2]: +[ + Currency(alpha3='HKD', code_num=344, name='Hong Kong dollar', + symbols=['HK$', 'HK$', '$', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', 'HK﹩', '﹩', '元'], minor=2, countries=['HK']), - Currency(alpha3='CNH', code_num=None, name='Chinese yuan (when traded offshore)', - symbols=['CN¥', '¥', 'CN¥', '¥', 'RMB', '元'], + Currency(alpha3='CNH', code_num=None, name='Chinese yuan (when traded offshore)', + symbols=['CN¥', '¥', 'CN¥', '¥', 'RMB', '元'], minor=2, countries=['HK']) ] @@ -158,10 +158,10 @@ Returns: In [1]: import iso4217parse In [2]: iso4217parse.by_symbol('$MN') -Out[2]: +Out[2]: [ - Currency(alpha3='CUP', code_num=192, name='Cuban peso', - symbols=['₱', '$', '﹩', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', '$MN', '﹩MN', '$MN'], + Currency(alpha3='CUP', code_num=192, name='Cuban peso', + symbols=['₱', '$', '﹩', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', '$MN', '﹩MN', '$MN'], minor=2, countries=['CU']) ] @@ -170,17 +170,17 @@ Out[3]: [...] # 35 different currencies In [4]: [c.alpha3 for c in iso4217parse.by_symbol('$')] Out[4]: -['ARS', 'AUD', 'BBD', 'BMD', 'BZD', 'SBD', 'BND', 'CAD', 'CVE', 'KYD', 'CLP', - 'COP', 'CUP', 'DOP', 'FJD', 'GYD', 'HKD', 'JMD', 'LRD', 'MXN', 'NAD', 'NZD', - 'SGD', 'TTD', 'USD', 'UYU', 'TWD', 'CUC', 'ZWL', 'XCD', 'SRD', 'BRL', 'KID', +['ARS', 'AUD', 'BBD', 'BMD', 'BZD', 'SBD', 'BND', 'CAD', 'CVE', 'KYD', 'CLP', + 'COP', 'CUP', 'DOP', 'FJD', 'GYD', 'HKD', 'JMD', 'LRD', 'MXN', 'NAD', 'NZD', + 'SGD', 'TTD', 'USD', 'UYU', 'TWD', 'CUC', 'ZWL', 'XCD', 'SRD', 'BRL', 'KID', 'NTD', 'TVD'] In [5]: iso4217parse.by_symbol('$', country_code='US') -Out[5]: +Out[5]: [ - Currency(alpha3='USD', code_num=840, name='United States dollar', - symbols=['US$', '$', '$', '﹩', 'dollar', 'dollars', 'Dollar', 'Dollars', 'US$', 'US﹩'], - minor=2, + Currency(alpha3='USD', code_num=840, name='United States dollar', + symbols=['US$', '$', '$', '﹩', 'dollar', 'dollars', 'Dollar', 'Dollars', 'US$', 'US﹩'], + minor=2, countries=['AS', 'EC', 'GU', 'HT', 'MH', 'MP', 'PR', 'PW', 'SV', 'TC', 'TL', 'UM', 'US']) ] @@ -206,33 +206,33 @@ Returns: In [1]: import iso4217parse In [2]: iso4217parse.by_symbol_match('RD$35.8') -Out[2]: +Out[2]: [ - Currency(alpha3='DOP', code_num=214, name='Dominican peso', - symbols=['RD$', '$', '﹩', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', 'RD$', 'RD﹩'], + Currency(alpha3='DOP', code_num=214, name='Dominican peso', + symbols=['RD$', '$', '﹩', '$', 'dollar', 'dollars', 'Dollar', 'Dollars', 'RD$', 'RD﹩'], minor=2, countries=['DO']) ] In [3]: iso4217parse.by_symbol_match('The price is ₨ 35.8 !') -Out[3]: +Out[3]: [ - Currency(alpha3='LKR', code_num=144, name='Sri Lankan rupee', - symbols=['රු', '₨', 'Rs', 'ரூ', 'SLRs', 'rupees', 'rupee'], + Currency(alpha3='LKR', code_num=144, name='Sri Lankan rupee', + symbols=['රු', '₨', 'Rs', 'ரூ', 'SLRs', 'rupees', 'rupee'], minor=2, countries=['LK']), - Currency(alpha3='MUR', code_num=480, name='Mauritian rupee', + Currency(alpha3='MUR', code_num=480, name='Mauritian rupee', symbols=['₨', 'rupees', 'rupee'], minor=2, countries=['MU']), - Currency(alpha3='NPR', code_num=524, name='Nepalese rupee', + Currency(alpha3='NPR', code_num=524, name='Nepalese rupee', symbols=['रु', '₨', 'Rs', 'Re', 'rupees', 'rupee'], minor=2, countries=['NP']), - Currency(alpha3='PKR', code_num=586, name='Pakistani rupee', - symbols=['₨', 'Rs', 'rupees', 'rupee'], + Currency(alpha3='PKR', code_num=586, name='Pakistani rupee', + symbols=['₨', 'Rs', 'rupees', 'rupee'], minor=2, countries=['PK']) ] In [4]: iso4217parse.by_symbol_match('The price is ₨ 35.8 !', country_code='NP') -Out[4]: +Out[4]: [ - Currency(alpha3='NPR', code_num=524, name='Nepalese rupee', - symbols=['रु', '₨', 'Rs', 'Re', 'rupees', 'rupee'], + Currency(alpha3='NPR', code_num=524, name='Nepalese rupee', + symbols=['रु', '₨', 'Rs', 'Re', 'rupees', 'rupee'], minor=2, countries=['NP']) ] diff --git a/gen_data.py b/gen_data.py index 7907299..a68a24f 100644 --- a/gen_data.py +++ b/gen_data.py @@ -1,6 +1,6 @@ # The MIT License -# Copyright (c) 2017 Tammo Ippen, tammo.ippen@posteo.de +# Copyright (c) 2017 - 2018 Tammo Ippen, tammo.ippen@posteo.de # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -93,7 +93,7 @@ # get active table active = [] -for row in tables[1].findAll('tr'): +for row in tables[1].findAll('tr'): # noqa tds = row.findAll('td') if tds: try: @@ -178,7 +178,7 @@ # ignore historical for now historical = [] -for row in tables[5].findAll('tr'): +for row in tables[5].findAll('tr'): # noqa tds = row.findAll('td') if tds: code = tds[1].text diff --git a/iso4217parse/__init__.py b/iso4217parse/__init__.py index 1fc24b6..2935361 100644 --- a/iso4217parse/__init__.py +++ b/iso4217parse/__init__.py @@ -3,7 +3,7 @@ # The MIT License -# Copyright (c) 2017 Tammo Ippen, tammo.ippen@posteo.de +# Copyright (c) 2017 - 2018 Tammo Ippen, tammo.ippen@posteo.de # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -38,6 +38,16 @@ from io import open +__all__ = [ + 'Currency', + 'by_alpha3', + 'by_code_num', + 'by_symbol', + 'by_symbol_match', + 'by_country', + 'parse', +] + Currency = namedtuple('Currency', [ 'alpha3', # unicode: the ISO4217 alpha3 code 'code_num', # int: the ISO4217 numeric code @@ -77,6 +87,12 @@ def _data(): for s, d in _DATA['symbol'].items(): _DATA['symbol'][s] = sorted(d, key=lambda d: 10000 if d.code_num is None else d.code_num) + _DATA['name'] = {} + for d in _DATA['alpha3'].values(): + if d.name in _DATA['name']: + assert 'Duplicate name "{}"!'.format(d.name) + _DATA['name'][d.name] = d + _DATA['country'] = defaultdict(list) for d in _DATA['alpha3'].values(): for cc in d.countries: @@ -103,9 +119,12 @@ def _symbols(): """ global _SYMBOLS if _SYMBOLS is None: + tmp = [(s, 'symbol') for s in _data()['symbol'].keys()] + tmp += [(s, 'alpha3') for s in _data()['alpha3'].keys()] + tmp += [(s.name, 'name') for s in _data()['alpha3'].values()] _SYMBOLS = sorted( - _data()['symbol'].keys(), - key=lambda s: (len(s), ord(s[0])), + tmp, + key=lambda s: (len(s[0]), ord(s[0][0])), reverse=True) return _SYMBOLS @@ -167,6 +186,8 @@ def by_symbol_match(value, country_code=None): Look for first matching currency symbol in `value`. Filter similar to `by_symbol`. Symbols are sorted by length and relevance of first character (see `_symbols()`). + Symbols are considered to be currency symbols, alpha3 codes or currency names. + Note: This is a [heuristic](https://en.wikipedia.org/wiki/Heuristic) ! Parameters: @@ -176,9 +197,15 @@ def by_symbol_match(value, country_code=None): Returns: List[Currency]: Currency objects found in `value`; filter by country_code. """ - for s in _symbols(): - if s in value: - res = by_symbol(s, country_code) + res = None + for s, group in _symbols(): + if s.lower() in value.lower(): + if group == 'symbol': + res = by_symbol(s, country_code) + if group == 'alpha3': + res = [by_alpha3(s)] + if group == 'name': + res = [_data()['name'][s]] if res: return res diff --git a/pyproject.toml b/pyproject.toml index 74b7d13..68d3e87 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "iso4217parse" -version = "0.4.2" +version = "0.5.0" description = "Parse currencies (symbols and codes) from and to ISO4217." authors = ["Tammo Ippen "] license = "MIT" @@ -30,22 +30,24 @@ classifiers=[ ] [tool.poetry.dependencies] -python = "*" +python = "~2.7 | ^3.5" [tool.poetry.dev-dependencies] +coveralls = "^1.5.1" flake8 = "^3.5.0" -flake8-import-order = "^0.18" +flake8-bugbear = { version = "^18.8.0", python = "^3.5" } flake8-commas = "^2.0.0" flake8-comprehensions = "^1.4.1" +flake8-import-order = "^0.18" flake8-pep3101 = "^1.2.1" flake8-polyfill = "^1.0.2" flake8-quotes = "^1.0.0" +funcsigs = { version = "^1.0", python = "~2.7" } iso3166 = "^0.9.0" pep8-naming = "^0.7" pytest = "^3.7.3" pytest-cov = "^2.5.1" pytest-flake8 = "^1.0.2" -python-coveralls = "^2.9.1" pytest-pythonpath = "^0.7.3" six = "^1.11.0" diff --git a/setup.cfg b/setup.cfg index 2cdaf51..61efb0c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,16 +1,21 @@ [flake8] +application_import_names = iso4217parse max-line-length = 159 max-complexity = 10 import-order-style = google -exclude = gen_data.py +exclude = + .venv/ + venv/ + gen_data.py [tool:pytest] python_paths = . -addopts = --cov=iso4217parse --cov-report=term-missing --flake8 -v -s +ignore = gen_data.py +addopts = --cov=iso4217parse --cov-report term-missing --cov-report html:cov_html -v [bdist_wheel] universal = 1 [metadata] -description-file = README.md license_file = LICENSE.txt +description-file = README.md diff --git a/tests/test_by_country.py b/tests/test_by_country.py index b1d712e..680f1d4 100644 --- a/tests/test_by_country.py +++ b/tests/test_by_country.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals import iso3166 + import iso4217parse @@ -18,8 +19,8 @@ def test_all_countries(): cs = iso4217parse.by_country(country.alpha2) if cs is None: print(country.alpha2, country) - # assert isinstance(cs, list) - # assert len(cs) > 0 + assert isinstance(cs, list) + assert len(cs) > 0 def test_examples(): diff --git a/tests/test_parse.py b/tests/test_parse.py index f42a81f..fe9f172 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -1,9 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals +import pytest + import iso4217parse as iso4217 +def test_invalid(): + for v in (None, [], {}, 3.14): + with pytest.raises(ValueError): + iso4217.parse(v) + + def test_examples(): exp = iso4217.Currency( alpha3='CHF', @@ -40,6 +48,10 @@ def test_examples(): ) assert [exp] == iso4217.parse('Price is 5 €') + assert [exp] == iso4217.parse('Price is 5 EUR') + assert [exp] == iso4217.parse('Price is 5 eur') + assert [exp] == iso4217.parse('Price is 5 euro') + assert [exp] == iso4217.parse('Price is 5 Euro') exp = iso4217.Currency( alpha3='CAD',