summaryrefslogtreecommitdiff
path: root/__init__.py
blob: 8bc424f09e21db9928a0a53e1ac5800eb2db4c8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# coding: utf-8


# < from ruamel.util.new import _convert_version
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", 2)
__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
    """
    class _AliasesChoicesPseudoAction(argparse.Action):

        def __init__(self, name, aliases, help):
            metavar = dest = name
            if aliases:
                metavar += ' (%s)' % ', '.join(aliases)
            sup = super(SubParsersAction._AliasesChoicesPseudoAction, self)
            sup.__init__(option_strings=[], dest=dest, help=help,
                         metavar=metavar)

    def add_parser(self, name, **kwargs):
        # 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)
            self._choices_actions.append(choice_action)
        if aliases is not None:
            for alias in aliases:
                self._name_parser_map[alias] = parser

        return parser

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


# decorators

# decorate method as a subparser, ToDo: signature
def sub_parser(*args, **kw):
    class Decorator(object):
        def __init__(self):
            self.target = None

        def __call__(self, target):
            self.target = target
            target.sub_parser = {'args': args, 'kw': kw}
            target.option = self.option
            return target

        def option(self, *a, **k):
            """ after a method xyz is decorated as sub_parser, you can add
            options with:
              @xyz.option('--force')
              def force(self):
                 pass

            if option argument is a short option ('-F'), add the long
            option based on the function name
            if no option argument, add function_name if 'nargs' in k
            else add long option based on function name
            """
            # here check if a is option name else take from function
            def option_func(*args):
                """called with the original function that is decorated
                add function name to last options element
                """
                self.target.options[-1]['fun'] =  args[0].__name__
                pass

            if not hasattr(self.target, "options"):
                self.target.options = []
            self.target.options.append({'args': a, 'kw': k})
            return option_func
    decorator = Decorator()
    return decorator


# global options can come before and after a sub_parser, ToDo: signature
def global_option(*args, **kw):
    def decorator(target):
        target.global_option = {'args': args, 'kw': kw}
        return target
    return decorator


# global only options can come before a sub_parser only, ToDo: signature
def global_only_option(*args, **kw):
    def decorator(target):
        target.global_only_option = {'args': args, 'kw': kw}
        return target
    return decorator


def create_argument_parser(self, *args, **keywords):
    argument_parser = argparse.ArgumentParser(**keywords)
    self._subparsers = None
    for x in dir(self):
        if x.startswith('_'):
            continue
        method = getattr(self, x)
        if hasattr(method, "sub_parser"):
            if self._subparsers is None:
                self._subparsers = argument_parser.add_subparsers(
                    dest="subparser_name", help='sub-command help')
            #(name, aliases=aliases, help=help)
            arg = method.sub_parser['args']
            if not arg or not isinstance(arg[0], basestring):
                arg = list(arg)
                arg.insert(0, x)
            sp = self._subparsers.add_parser(*arg,
                                             **method.sub_parser['kw'])
            sp.set_defaults(func=method)

            #print x, method.sub_parser
            if hasattr(method, "options"):
                for o in method.options:
                    #print 'arg1', o
                    arg = list(o['args'])
                    fun_name = o.get('fun')
                    if arg:
                        # short option name only, add long option name
                        # based on function name
                        if len(arg[0]) == 2 and arg[0][0] == '-':
                            if (fun_name):
                                arg.insert(0, '--' + fun_name)
                    else:
                        # no option name
                        if o['kw'].get('nargs') == '+':
                            # file names etc, no leading dashes
                            arg.insert(0, fun_name)
                        else:
                            # add long option based on function name
                            arg.insert(0, '--' + fun_name)
                    #print 'arg2', arg
                    sp.add_argument(*arg, **o['kw'])
                #print '    opt:', x, method.options
        if hasattr(method, "global_only_option"):
            arg = method.global_only_option['args']
            kw = method.global_only_option['kw']
            argument_parser.add_argument(*arg, **kw)
    for x in dir(self):
        if x.startswith('_'):
            continue
        method = getattr(self, x)
        if hasattr(method, "global_option"):
            for name in self._subparsers._name_parser_map:
                sp = self._subparsers._name_parser_map[name]
                arg = method.global_option['args']
                if arg and not arg[0].startswith('--'):
                    arg = list(arg)
                    arg.insert(0, '--' + x)
                sp.add_argument(*arg, **method.global_option['kw'])
    return argument_parser