Passed
Push — develop ( 4d6495...2dddb7 )
by Jace
03:19
created

gitman.cli.main()   B

Complexity

Conditions 2

Size

Total Lines 112
Code Lines 84

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 51
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 84
nop 2
dl 0
loc 112
ccs 51
cts 51
cp 1
crap 2
rs 7.5236
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
#!/usr/bin/env python3
2
3 1
"""Command-line interface."""
4
5 1
import argparse
6 1
import logging
7 1
import sys
8
9 1
from . import CLI, DESCRIPTION, VERSION, commands, common, exceptions
10 1
11
12 1
log = logging.getLogger(__name__)
13
14
15 1
def main(args=None, function=None):  # pylint: disable=too-many-statements
16
    """Process command-line arguments and run the program."""
17
18
    # Shared options
19 1
    debug = argparse.ArgumentParser(add_help=False)
20 1
    debug.add_argument('-V', '--version', action='version', version=VERSION)
21 1
    group = debug.add_mutually_exclusive_group()
22 1
    group.add_argument('-v', '--verbose', action='count', default=0,
23
                       help="enable verbose logging")
24 1
    group.add_argument('-q', '--quiet', action='store_const', const=-1,
25
                       dest='verbose', help="only display errors and prompts")
26 1
    project = argparse.ArgumentParser(add_help=False)
27 1
    project.add_argument('-r', '--root', metavar='PATH',
28
                         help="root directory of the project")
29 1
    depth = argparse.ArgumentParser(add_help=False)
30 1
    depth.add_argument('-d', '--depth', type=common.positive_int,
31
                       default=5, metavar="NUM",
32
                       help="limit the number of dependency levels")
33 1
    options = argparse.ArgumentParser(add_help=False)
34 1
    options.add_argument('-f', '--force', action='store_true',
35
                         help="overwrite uncommitted changes in dependencies")
36 1
    options.add_argument('-c', '--clean', action='store_true',
37
                         help="delete ignored files in dependencies")
38 1
    shared = {'formatter_class': common.WideHelpFormatter}
39
40
    # Main parser
41 1
    parser = argparse.ArgumentParser(prog=CLI, description=DESCRIPTION,
42
                                     parents=[debug], **shared)
43 1
    subs = parser.add_subparsers(help="", dest='command', metavar="<command>")
44
45
    # Init parser
46 1
    info = "create a new config file for the project"
47 1
    sub = subs.add_parser('init', description=info.capitalize() + '.',
48
                          help=info, parents=[debug], **shared)
49
50
    # Install parser
51 1
    info = "get the specified versions of all dependencies"
52 1
    sub = subs.add_parser('install', description=info.capitalize() + '.',
53
                          help=info, parents=[debug, project, depth, options],
54
                          **shared)
55 1
    sub.add_argument('name', nargs='*',
56
                     help="list of dependencies names to install")
57 1
    sub.add_argument('-e', '--fetch', action='store_true',
58
                     help="always fetch the latest branches")
59
60
    # Update parser
61 1
    info = "update dependencies to the latest versions"
62 1
    sub = subs.add_parser('update', description=info.capitalize() + '.',
63
                          help=info, parents=[debug, project, depth, options],
64
                          **shared)
65 1
    sub.add_argument('name', nargs='*',
66
                     help="list of dependencies names to update")
67 1
    sub.add_argument('-a', '--all', action='store_true', dest='recurse',
68
                     help="also update all nested dependencies")
69 1
    sub.add_argument('-L', '--skip-lock',
70 1
                     action='store_false', dest='lock', default=None,
71
                     help="disable recording of updated versions")
72
73 1
    # List parser
74
    info = "display the current version of each dependency"
75
    sub = subs.add_parser('list', description=info.capitalize() + '.',
76
                          help=info, parents=[debug, project, depth], **shared)
77
    sub.add_argument('-D', '--fail-if-dirty', action='store_false',
78 1
                     dest='allow_dirty',
79 1
                     help="fail if a source has uncommitted changes")
80
81 1
    # Lock parser
82
    info = "lock the current version of each dependency"
83
    sub = subs.add_parser('lock', description=info.capitalize() + '.',
84
                          help=info, parents=[debug, project], **shared)
85
    sub.add_argument('name', nargs='*',
86 1
                     help="list of dependency names to lock")
87 1
88
    # Uninstall parser
89 1
    info = "delete all installed dependencies"
90
    sub = subs.add_parser('uninstall', description=info.capitalize() + '.',
91
                          help=info, parents=[debug, project], **shared)
92
    sub.add_argument('-f', '--force', action='store_true',
93 1
                     help="delete uncommitted changes in dependencies")
94 1
    sub.add_argument('-k', '--keep-location', dest='keep_location',
95
                     default=False, action='store_true',
96 1
                     help="keep top level folder location")
97
98
    # Show parser
99
    info = "display the path of a dependency or internal file"
100 1
    sub = subs.add_parser('show', description=info.capitalize() + '.',
101 1
                          help=info, parents=[debug, project], **shared)
102
    sub.add_argument('name', nargs='*',
103 1
                     help="display the path of this dependency")
104
    sub.add_argument('-c', '--config', action='store_true',
105 1
                     help="display the path of the config file")
106
    sub.add_argument('-l', '--log', action='store_true',
107 1
                     help="display the path of the log file")
108
109
    # Edit parser
110
    info = "open the config file in the default editor"
111 1
    sub = subs.add_parser('edit', description=info.capitalize() + '.',
112 1
                          help=info, parents=[debug, project], **shared)
113
114
    # Parse arguments
115
    namespace = parser.parse_args(args=args)
116 1
117
    # Configure logging
118
    common.configure_logging(namespace.verbose)
119 1
120
    # Run the program
121
    function, args, kwargs = _get_command(function, namespace)
122 1
    if function:
123 1
        _run_command(function, args, kwargs)
124 1
    else:
125
        parser.print_help()
126 1
        sys.exit(1)
127 1
128
129
def _get_command(function, namespace):  # pylint: disable=too-many-statements
130 1
    args = []
131 1
    kwargs = {}
132 1
133
    if namespace.command == 'init':
134 1
        function = commands.init
135 1
136
    elif namespace.command in ['install', 'update']:
137 1
        function = getattr(commands, namespace.command)
138 1
        args = namespace.name
139 1
        kwargs.update(root=namespace.root,
140 1
                      depth=namespace.depth,
141
                      force=namespace.force,
142
                      clean=namespace.clean)
143
        if namespace.command == 'install':
144 1
            kwargs.update(fetch=namespace.fetch)
145 1
        if namespace.command == 'update':
146 1
            kwargs.update(recurse=namespace.recurse,
147 1
                          lock=namespace.lock)
148
149
    elif namespace.command == 'list':
150 1
        function = commands.display
151 1
        kwargs.update(root=namespace.root,
152 1
                      depth=namespace.depth,
153
                      allow_dirty=namespace.allow_dirty)
154
155
    elif namespace.command == 'lock':
156 1
        function = getattr(commands, namespace.command)
157 1
        args = namespace.name
158 1
        kwargs.update(root=namespace.root)
159 1
160
    elif namespace.command == 'uninstall':
161 1
        function = commands.delete
162 1
        kwargs.update(root=namespace.root,
163 1
                      force=namespace.force,
164
                      keep_location=namespace.keep_location)
165
166 1
    elif namespace.command == 'show':
167 1
        function = commands.show
168 1
        args = namespace.name
169 1
        kwargs.update(root=namespace.root)
170 1
        if namespace.config:
171 1
            args.append('__config__')
172 1
        if namespace.log:
173 1
            args.append('__log__')
174
175 1
    elif namespace.command == 'edit':
176 1
        function = commands.edit
177 1
        kwargs.update(root=namespace.root)
178
179 1
    return function, args, kwargs
180
181
182 1
def _run_command(function, args, kwargs):
183 1
    success = False
184 1
    exit_message = None
185 1
    try:
186 1
        log.debug("Running %s command...", getattr(function, '__name__', 'a'))
187 1
        success = function(*args, **kwargs)
188 1
    except KeyboardInterrupt:
189 1
        log.debug("Command canceled")
190 1
    except exceptions.UncommittedChanges as exception:
191 1
        _show_error(exception)
192 1
        exit_message = "Run again with '--force' to discard changes"
193 1
    except exceptions.ScriptFailure as exception:
194 1
        _show_error(exception)
195 1
        exit_message = "Run again with '--force' to ignore script errors"
196
    finally:
197 1
        if exit_message:
198 1
            common.show(exit_message, color='message')
199 1
            common.newline()
200
201 1
    if success:
202 1
        log.debug("Command succeeded")
203
    else:
204 1
        log.debug("Command failed")
205 1
        sys.exit(1)
206
207
208 1
def _show_error(exception):
209
    # TODO: require level=, evaluate all calls to dedent()
210 1
    common.dedent(0)
211 1
    common.newline()
212 1
    common.show(str(exception), color='error')
213 1
    common.newline()
214
215
216
if __name__ == '__main__':  # pragma: no cover (manual test)
217
    main()
218