diff options
author | Anthon van der Neut <anthon@mnt.org> | 2014-03-03 21:18:44 +0100 |
---|---|---|
committer | Anthon van der Neut <anthon@mnt.org> | 2014-03-03 21:18:44 +0100 |
commit | d569ebeb9870331d739ab434dd9a99dfc4529a09 (patch) | |
tree | 6536c95d5089ea3e90eb6e2e3406d5787ca3538e | |
parent | 30dbd5babf56398c16efd82f513935d98e36a81d (diff) | |
download | ruamel.std.argparse-d569ebeb9870331d739ab434dd9a99dfc4529a09.tar.gz |
add SmartFormatter and tests
-rw-r--r-- | .hgignore | 1 | ||||
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | __init__.py | 68 | ||||
-rw-r--r--[-rwxr-xr-x] | setup.py | 101 | ||||
-rw-r--r-- | test/test_argparse.py | 101 | ||||
-rw-r--r-- | tox.ini | 7 |
6 files changed, 256 insertions, 32 deletions
@@ -5,6 +5,7 @@ syntax: glob *~ *.bak *.o +*.orig dist build *.egg-info @@ -1,7 +1,9 @@ +UTILNAME:=argparse +PKGNAME:=ruamel.std.argparse VERSION:=$$(python setup.py --version) -DIST:=dist/ruamel.std.argparse-$(VERSION).tar.gz -REGEN:=/usr/local/bin/ruamel_util_new util --std argparse --skip-hg --skip-util +DIST:=dist/$(PKGNAME)-$(VERSION).tar.gz +REGEN:=/usr/local/bin/ruamel_util_new util --std argparse --skip-util --skip-hg sdist: python setup.py sdist @@ -10,7 +12,7 @@ wheel: python setup.py bdist_wheel clean: - rm -rf build *.egg-info/ + rm -rf build .tox $(PKGNAME).egg-info/ find . -name "*.pyc" -exec rm {} + tar: @@ -25,6 +27,6 @@ regen_setup: pep8 setup.py regen_makefile: - rm -f ../argparse/Makefile + rm -f ../$(UTILNAME)/Makefile $(REGEN) diff --git a/__init__.py b/__init__.py index 27847d1..0796ac7 100644 --- a/__init__.py +++ b/__init__.py @@ -1,8 +1,32 @@ # coding: utf-8 + +def _convert_version(tup): + """create a PEP 386 pseudo-format conformant string from tuple tup""" + ret_val = str(tup[0]) # first is always digit + next_sep = "." # separator for next extension, can be "" or "." + for x in tup[1:]: + if isinstance(x, int): + ret_val += next_sep + str(x) + next_sep = '.' + continue + first_letter = x[0].lower() + next_sep = '' + if first_letter in 'abcr': + ret_val += 'rc' if first_letter == 'r' else first_letter + elif first_letter in 'pd': + ret_val += '.post' if first_letter == 'p' else '.dev' + return ret_val + +version_info = (0, 2, "alpha", 1) +__version__ = _convert_version(version_info) + +del _convert_version + import argparse from argparse import ArgumentParser + class SubParsersAction(argparse._SubParsersAction): """support aliases, based on differences of 3.3 and 2.7 """ @@ -17,13 +41,15 @@ class SubParsersAction(argparse._SubParsersAction): metavar=metavar) def add_parser(self, name, **kwargs): - # remove aliases and help kwargs so orginal add_parser does not get them + # remove aliases and help kwargs so the orginal add_parser + # does not get them aliases = kwargs.pop('aliases', ()) help = kwargs.pop('help', None) parser = argparse._SubParsersAction.add_parser(self, name, **kwargs) if help is not None: - choice_action = self._AliasesChoicesPseudoAction(name, aliases, help) + choice_action = self._AliasesChoicesPseudoAction(name, aliases, + help) self._choices_actions.append(choice_action) if aliases is not None: for alias in aliases: @@ -34,3 +60,41 @@ class SubParsersAction(argparse._SubParsersAction): from _action.checksinglestore import CheckSingleStoreAction from _action.count import CountAction from _action.splitappend import SplitAppendAction + + +class SmartFormatter(argparse.HelpFormatter): + """ + you can only specify one formatter in standard argparse, so you cannot + both have pre-formatted description (RawDescriptionHelpFormatter) + and ArgumentDefaultsHelpFormatter. + The SmartFormatter has sensible defaults (RawDescriptionFormatter) and + the individual help text can be marked ( help="R|" ) for + variations in formatting. + """ + def __init__(self, *args, **kw): + self._add_defaults = False + super(SmartFormatter, self).__init__(*args, **kw) + + def _fill_text(self, text, width, indent): + return ''.join([indent + line for line in text.splitlines(True)]) + + + def _split_lines(self, text, width): + #print 'TEXT', text + if text.startswith('D|'): + self._add_defaults = True + text = text[2:] + if text.startswith('R|'): + return text[2:].splitlines() + return argparse.HelpFormatter._split_lines(self, text, width) + + def _get_help_string(self, action): + if not self._add_defaults: + return argparse.HelpFormatter._get_help_string(self, action) + help = action.help + if '%(default)' not in action.help: + if action.default is not argparse.SUPPRESS: + defaulting_nargs = [argparse.OPTIONAL, argparse.ZERO_OR_MORE] + if action.option_strings or action.nargs in defaulting_nargs: + help += ' (default: %(default)s)' + return help @@ -1,26 +1,77 @@ #! /usr/bin/env python # coding: utf-8 +from __future__ import print_function + import sys import os -from setuptools import setup, find_packages -from setuptools.command import install_lib from textwrap import dedent name_space = 'ruamel' -full_package_name = name_space + '.std.argparse' +package_name = 'argparse' +full_package_name = name_space + '.std.' + package_name exclude_files = [ - 'setup.py' + 'setup.py', ] + +def get_version(): + v_i = 'version_info = ' + for line in open('__init__.py'): + if not line.startswith(v_i): + continue + s_e = line[len(v_i):].strip()[1:-1].split(', ') + els = [x.strip()[1:-1] if x[0] in '\'"' else int(x) for x in s_e] + return els + + +def _check_convert_version(tup): + """create a PEP 386 pseudo-format conformant string from tuple tup""" + ret_val = str(tup[0]) # first is always digit + next_sep = "." # separator for next extension, can be "" or "." + nr_digits = 0 # nr of adjacent digits in rest, to verify + post_dev = False # are we processig post/dev + for x in tup[1:]: + if isinstance(x, int): + nr_digits += 1 + if nr_digits > 2: + raise ValueError("to many consecutive digits " + ret_val) + ret_val += next_sep + str(x) + next_sep = '.' + continue + first_letter = x[0].lower() + next_sep = '' + if first_letter in 'abcr': + if post_dev: + raise ValueError("release level specified after " + "post/dev:" + x) + nr_digits = 0 + ret_val += 'rc' if first_letter == 'r' else first_letter + elif first_letter in 'pd': + nr_digits = 1 # only one can follow + post_dev = True + ret_val += '.post' if first_letter == 'p' else '.dev' + else: + raise ValueError('First letter of "' + x + '" not recognised') + return ret_val + +version_info = get_version() +version_str = _check_convert_version(version_info) + +if __name__ == '__main__': + # put here so setup.py can be imported more easily + from setuptools import setup, find_packages, Extension + from setuptools.command import install_lib + + class MyInstallLib(install_lib.install_lib): "create __init__.py on the fly" def run(self): install_lib.install_lib.run(self) init_txt = dedent('''\ # coding: utf-8 - # Copyright © 2013 Anthon van der Neut, RUAMEL bvba + # Copyright © 2013-2014 Anthon van der Neut, RUAMEL bvba "generated __init__.py " try: __import__('pkg_resources').declare_namespace(__name__) @@ -30,8 +81,8 @@ class MyInstallLib(install_lib.install_lib): init_path = full_package_name.split('.')[:-1] for product_init in [ os.path.join( - *([self.install_dir] + init_path[:p+1] + ['__init__.py'])) for p - in range(len(init_path)) + *([self.install_dir] + init_path[:p+1] + ['__init__.py'])) + for p in range(len(init_path)) ]: if not os.path.exists(product_init): print('creating %s' % product_init) @@ -41,7 +92,8 @@ class MyInstallLib(install_lib.install_lib): def install(self): fpp = full_package_name.split('.') # full package path - full_exclude_files = [os.path.join(*(fpp + [x])) for x in exclude_files] + full_exclude_files = [os.path.join(*(fpp + [x])) + for x in exclude_files] alt_files = [] outfiles = install_lib.install_lib.install(self) for x in outfiles: @@ -51,35 +103,32 @@ class MyInstallLib(install_lib.install_lib): break else: alt_files.append(x) - for x in alt_files: - print ' ', x.split('site-packages/')[-1] - return alt_files def main(): install_requires = [ - ] - packages = [full_package_name] + [(full_package_name + '.' + x) for x \ - in find_packages(exclude=['tests'])] + ] + #if sys.version_info < (3, 4): + # install_requires.append("") + packages = [full_package_name] + [(full_package_name + '.' + x) for x + in find_packages(exclude=['tests'])] setup( name=full_package_name, - version="0.1.1", + version=version_str, description="Documentation for " + full_package_name, - install_requires=[ - # 'configobj >=4.7.2', - ], - #install_requires=install_requires, - long_description="Long Description", - url='https://bitbucket.org/anthon_van_der_neut/' + full_package_name, + install_requires=install_requires, + long_description=open('README.rst').read(), + url='https://bitbucket.org/ruamel/' + package_name, author='Anthon van der Neut', author_email='a.van.der.neut@ruamel.eu', - license='Copyright Ruamel bvba 2007-2013', + license="Copyright Ruamel bvba 2007-2014", package_dir={full_package_name: '.'}, - namespace_packages = [name_space], + namespace_packages=[name_space], packages=packages, cmdclass={'install_lib': MyInstallLib}, - classifiers=['Development Status :: 4 - Beta', + classifiers=[ + 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: Copyrighted', 'Operating System :: OS Independent', @@ -89,7 +138,7 @@ def main(): if __name__ == '__main__': if len(sys.argv) > 1 and sys.argv[1] == 'sdist': - assert full_package_name == os.path.abspath(os.path.dirname( + assert full_package_name == os.path.abspath(os.path.dirname( __file__)).split('site-packages' + os.path.sep)[1].replace( - os.path.sep, '.') + os.path.sep, '.') main() diff --git a/test/test_argparse.py b/test/test_argparse.py new file mode 100644 index 0000000..1fab404 --- /dev/null +++ b/test/test_argparse.py @@ -0,0 +1,101 @@ + +import pytest + +from ruamel.std.argparse import argparse, CountAction, SmartFormatter +from textwrap import dedent + +def exit(self=None, status=None, message=None): + pass + +# default for tox stub is to Fail +def test_argparse(capsys): + desc = dedent("""\ + Please do not mess up this text! + -------------------------------- + I have indented it + exactly the way + I want it + """) + help_verbose = "add some verbosity to the output" + help_list = """\ + choose one: + 1) red + 2) green + 3) blue + """ + help_one = """one + line + help + """ + parser = argparse.ArgumentParser( + description=desc, + formatter_class=SmartFormatter, + ) + parser.exit = exit + parser.add_argument('--verbose', action='store_true', + help=help_verbose) + parser.add_argument('--list', help='R|' + dedent(help_list)) + parser.add_argument('--oneline', action='store_true', help=help_one) + parser.parse_args(['--help']) + out, err = capsys.readouterr() + full_help = dedent("""\ + usage: py.test [-h] [--verbose] [--list LIST] [--oneline] + + {} + optional arguments: + -h, --help show this help message and exit + --verbose {} + --list LIST {} + --oneline one line help + """).format( + desc, help_verbose, + help_list.lstrip().replace('\n ', '\n ').rstrip(), + ) + assert full_help == out + +def test_argparse_default(capsys): + desc = dedent("""\ + Please do not mess up this text! + -------------------------------- + I have indented it + exactly the way + I want it + """) + help_verbose = "add some verbosity to the output" + help_list = """\ + choose one: + 1) red + 2) green + 3) blue + """ + help_one = """one + line + help + """ + parser = argparse.ArgumentParser( + description=desc, + formatter_class=SmartFormatter, + ) + parser.exit = exit + # add "D|" to the first option + parser.add_argument('--verbose', action='store_true', + help='D|' + help_verbose) + parser.add_argument('--list', help='R|' + dedent(help_list)) + parser.add_argument('--oneline', action='store_true', help=help_one) + parser.parse_args(['--help']) + out, err = capsys.readouterr() + full_help = dedent("""\ + usage: py.test [-h] [--verbose] [--list LIST] [--oneline] + + {} + optional arguments: + -h, --help show this help message and exit + --verbose {} (default: False) + --list LIST {} + (default: None) + --oneline one line help (default: False) + """).format( + desc, help_verbose, + help_list.lstrip().replace('\n ', '\n ').rstrip(), + ) + assert full_help == out @@ -0,0 +1,7 @@ +[tox] +envlist = py27 + +[testenv] +commands = py.test test +deps = + pytest |