From 62f3cad2d674ceadaac55edd33ca691a85f583f3 Mon Sep 17 00:00:00 2001 From: Ernesto Rodriguez Ortiz Date: Thu, 19 Apr 2018 09:03:21 -0400 Subject: [PATCH] Add a command line tool to use num2words, credits to @hernamesbarbara --- .coveragerc | 4 ++ MANIFEST.in | 1 + README.rst | 14 ++++++- bin/num2words | 81 ++++++++++++++++++++++++++++++++++++ requirements-test.txt | 2 +- setup.py | 28 ++++++++++++- tests/test_cli.py | 96 +++++++++++++++++++++++++++++++++++++++++++ tox.ini | 4 +- 8 files changed, 224 insertions(+), 6 deletions(-) create mode 100644 .coveragerc create mode 100755 bin/num2words create mode 100644 tests/test_cli.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..9d3f399 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[report] +omit = + */.tox/* + */tests/* diff --git a/MANIFEST.in b/MANIFEST.in index 6822064..a13f180 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include CHANGES.rst include COPYING include tests/* +include bin/num2words diff --git a/README.rst b/README.rst index 51e2d23..2a9e494 100644 --- a/README.rst +++ b/README.rst @@ -37,8 +37,20 @@ The test suite in this library is new, so it's rather thin, but it can be run wi Usage ----- +Command line:: -There's only one function to use:: + $ num2words 10001 + ten thousand and one + $ num2words 10123123 --lang es + diez millones ciento veintitrés mil ciento veintitrés + $ num2words 24,120.10 + twenty-four thousand, one hundred and twenty point one + $ num2words 24,120.10 -l es + veinticuatro mil ciento veinte punto uno + $num2words 2.14 -l es --to currency + dos euros con catorce centimos + +In code there's only one function to use:: >>> from num2words import num2words >>> num2words(42) diff --git a/bin/num2words b/bin/num2words new file mode 100755 index 0000000..9c6df8a --- /dev/null +++ b/bin/num2words @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""num2words: convert numbers into words. + +Usage: + num2words [options] + num2words --list-languages + num2words --list-converters + num2words --help + +Arguments: + Number you want to convert into words + +Options: + -L --list-languages Show all languages. + -C --list-converters Show all converters. + -l --lang= Output language [default: en]. + -t --to= Output converter [default: cardinal]. + -h --help Show this message. + -v --version Show version. + +Examples: + $ num2words 10001 + ten thousand and one + + $ num2words 10123123 --lang es + diez millones ciento veintitrés mil ciento veintitrés + + $ num2words 24,120.10 + twenty-four thousand, one hundred and twenty point one + + $ num2words 24,120.10 -l es + veinticuatro mil ciento veinte punto uno + + $num2words 2.14 -l es --to currency + dos euros con catorce centimos +""" +from __future__ import print_function, unicode_literals +import os +import sys +from docopt import docopt +import num2words + +__version__ = "0.5.7" +__license__ = "LGPL" + + +def get_languages(): + return sorted(list(num2words.CONVERTER_CLASSES.keys())) + + +def get_converters(): + return sorted(list(num2words.CONVERTES_TYPES)) + + +def main(): + version = "{}=={}".format(os.path.basename(__file__), __version__) + args = docopt(__doc__, argv=None, help=True, version=version, options_first=False) + if args["--list-languages"]: + for lang in get_languages(): + sys.stdout.write(lang) + sys.stdout.write(os.linesep) + sys.exit(0) + if args["--list-converters"]: + for lang in get_converters(): + sys.stdout.write(lang) + sys.stdout.write(os.linesep) + sys.exit(0) + try: + words = num2words.num2words(args[''], lang=args['--lang'], to=args['--to']) + sys.stdout.write(words+os.linesep) + sys.exit(0) + except Exception as err: + sys.stderr.write(str(args[''])) + sys.stderr.write(str(err) + os.linesep) + sys.stderr.write(__doc__) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/requirements-test.txt b/requirements-test.txt index 9c4ea11..7c337d4 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -4,4 +4,4 @@ isort pep8<1.6 coverage coveralls - +delegator.py diff --git a/setup.py b/setup.py index 76f1c7a..5d27392 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,9 @@ +import re + from setuptools import find_packages, setup +PACKAGE_NAME = "num2words" + CLASSIFIERS = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', @@ -16,9 +20,27 @@ CLASSIFIERS = [ LONG_DESC = open('README.rst', 'rt').read() + '\n\n' + \ open('CHANGES.rst', 'rt').read() + +def find_version(fname): + """Parse file & return version number matching 0.0.1 regex + Returns str or raises RuntimeError + """ + version = '' + with open(fname, 'r') as fp: + reg = re.compile(r'__version__ = [\'"]([^\'"]*)[\'"]') + for line in fp: + m = reg.match(line) + if m: + version = m.group(1) + break + if not version: + raise RuntimeError('Cannot find version information') + return version + + setup( - name='num2words', - version='0.5.7', + name=PACKAGE_NAME, + version=find_version("bin/num2words"), description='Modules to convert numbers to words. Easily extensible.', long_description=LONG_DESC, license='LGPL', @@ -33,4 +55,6 @@ setup( packages=find_packages(exclude=['tests']), test_suite='tests', classifiers=CLASSIFIERS, + scripts=['bin/num2words'], + install_requires=["docopt>=0.6.2"] ) diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..3e1d3ab --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import unicode_literals + +import os +import unittest + +import delegator +import num2words + + +class CliCaller(object): + + def __init__(self): + self.cmd = os.path.realpath(os.path.join(os.path.dirname(__file__), + "..", "bin", "num2words")) + self.cmd_list = ["python", self.cmd] + + def run_cmd(self, *args): + cmd_list = self.cmd_list + [str(arg) for arg in args] + cmd = " ".join(cmd_list) + return delegator.run(cmd) + + +class CliTestCase(unittest.TestCase): + """Test the command line app""" + + def setUp(self): + self.cli = CliCaller() + + def test_cli_help(self): + """num2words without arguments should exit with status 1 + and show docopt's default short usage message + """ + output = self.cli.run_cmd() + self.assertEqual(output.return_code, 1) + self.assertTrue(output.err.startswith('Usage:')) + + def test_cli_list_langs(self): + """You should be able to list all availabe languages + """ + output = self.cli.run_cmd('--list-languages') + self.assertEqual( + sorted(list(num2words.CONVERTER_CLASSES.keys())), + output.out.strip().split(os.linesep) + ) + output = self.cli.run_cmd('-L') + self.assertEqual( + sorted(list(num2words.CONVERTER_CLASSES.keys())), + output.out.strip().split(os.linesep) + ) + + def test_cli_list_converters(self): + """You should be able to list all available converters + """ + output = self.cli.run_cmd('--list-converters') + self.assertEqual( + sorted(list(num2words.CONVERTES_TYPES)), + output.out.strip().split(os.linesep) + ) + output = self.cli.run_cmd('-C') + self.assertEqual( + sorted(list(num2words.CONVERTES_TYPES)), + output.out.strip().split(os.linesep) + ) + + def test_cli_default_lang(self): + """Default to english + """ + output = self.cli.run_cmd(150) + self.assertEqual(output.return_code, 0) + self.assertEqual( + output.out.strip(), + "one hundred and fifty point zero" + ) + + def test_cli_with_lang(self): + """You should be able to specify a language + """ + output = self.cli.run_cmd(150, '--lang', 'es') + self.assertEqual(output.return_code, 0) + self.assertEqual( + output.out.strip(), + "ciento cincuenta punto cero" + ) + + def test_cli_with_lang_to(self): + """You should be able to specify a language + """ + output = self.cli.run_cmd(150.55, '--lang', 'es', '--to', 'currency') + self.assertEqual(output.return_code, 0) + self.assertEqual( + output.out.strip(), + "ciento cincuenta euros con cincuenta y cinco centimos" + ) diff --git a/tox.ini b/tox.ini index 70d5de5..6bdc5f4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = flake8, isort, py27,py34,py35,py36 +envlist = flake8,isort,py27,py34,py35,py36 [testenv] passenv = TRAVIS TRAVIS_* @@ -10,6 +10,6 @@ commands = isort --check-only --recursive --diff num2words tests coverage erase coverage run -m unittest discover - coverage report --fail-under=75 --omit=.tox/*,/usr/* + coverage report --fail-under=75 --omit=.tox/*,tests/*,/usr/* coveralls