Completed
Pull Request — develop (#112)
by Jace
06:16
created

gitman._get_command()   F

Complexity

Conditions 11

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 11
Metric Value
cc 11
dl 0
loc 39
ccs 32
cts 32
cp 1
crap 11
rs 3.1764

How to fix   Complexity   

Complexity

Complex classes like gitman._get_command() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python3
2
3 1
"""Command-line interface."""
4
5 1
import sys
6 1
import argparse
7 1
import logging
8
9 1
from . import CLI, VERSION, DESCRIPTION
10 1
from . import common
11 1
from . import commands
12
13 1
log = logging.getLogger(__name__)
14
15
16 1
def main(args=None, function=None):
17
    """Process command-line arguments and run the program."""
18
19
    # Shared options
20 1
    debug = argparse.ArgumentParser(add_help=False)
21 1
    debug.add_argument('-V', '--version', action='version', version=VERSION)
22 1
    group = debug.add_mutually_exclusive_group()
23 1
    group.add_argument('-v', '--verbose', action='count', default=0,
24
                       help="enable verbose logging")
25 1
    group.add_argument('-q', '--quiet', action='store_const', const=-1,
26
                       dest='verbose', help="only display errors and prompts")
27 1
    project = argparse.ArgumentParser(add_help=False)
28 1
    project.add_argument('-r', '--root', metavar='PATH',
29
                         help="root directory of the project")
30 1
    depth = argparse.ArgumentParser(add_help=False)
31 1
    depth.add_argument('-d', '--depth', type=common.positive_int,
32
                       default=5, metavar="NUM",
33
                       help="limit the number of dependency levels")
34 1
    options = argparse.ArgumentParser(add_help=False)
35 1
    options.add_argument('-f', '--force', action='store_true',
36
                         help="overwrite uncommitted changes in dependencies")
37 1
    options.add_argument('-c', '--clean', action='store_true',
38
                         help="keep ignored files in dependencies")
39 1
    shared = {'formatter_class': common.WideHelpFormatter}
40
41
    # Main parser
42 1
    parser = argparse.ArgumentParser(prog=CLI, description=DESCRIPTION,
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
43
                                     parents=[debug, project], **shared)
44
45 1
    subs = parser.add_subparsers(help="", dest='command', metavar="<command>")
46
47
    # Install parser
48 1
    info = "get the specified versions of all dependencies"
49 1
    sub = subs.add_parser('install', description=info.capitalize() + '.',
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
50
                          help=info, parents=[debug, project, depth, options],
51
                          **shared)
52 1
    sub.add_argument('name', nargs='*',
53
                     help="list of dependencies names to install")
54 1
    sub.add_argument('-e', '--fetch', action='store_true',
55
                     help="always fetch the latest branches")
56
57
    # Update parser
58 1
    info = "update dependencies to the latest versions"
59 1
    sub = subs.add_parser('update', description=info.capitalize() + '.',
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
60
                          help=info, parents=[debug, project, depth, options],
61
                          **shared)
62 1
    sub.add_argument('name', nargs='*',
63
                     help="list of dependencies names to update")
64 1
    sub.add_argument('-a', '--all', action='store_true', dest='recurse',
65
                     help="update all nested dependencies, recursively")
66 1
    group = sub.add_mutually_exclusive_group()
67 1
    group.add_argument('-l', '--lock',
68
                       action='store_true', dest='lock', default=None,
69
                       help="enable recording of versions for later reinstall")
70 1
    group.add_argument('-L', '--no-lock',
71
                       action='store_false', dest='lock', default=None,
72
                       help="disable recording of versions for later reinstall")
73
74
    # Display parser
75 1
    info = "display the current version of each dependency"
76 1
    sub = subs.add_parser('list', description=info.capitalize() + '.',
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
77
                          help=info, parents=[debug, project, depth], **shared)
78 1
    sub.add_argument('-D', '--no-dirty', action='store_false',
79
                     dest='allow_dirty',
80
                     help="fail if a source has uncommitted changes")
81
82
    # Lock parser
83 1
    info = "lock the current version of each dependency"
84 1
    sub = subs.add_parser('lock', description=info.capitalize() + '.',
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
85
                          help=info, parents=[debug, project], **shared)
86 1
    sub.add_argument('name', nargs='*',
87
                     help="list of dependency names to lock")
88
89
    # Uninstall parser
90 1
    info = "delete all installed dependencies"
91 1
    sub = subs.add_parser('uninstall', description=info.capitalize() + '.',
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
92
                          help=info, parents=[debug, project], **shared)
93 1
    sub.add_argument('-f', '--force', action='store_true',
94
                     help="delete uncommitted changes in dependencies")
95
96
    # Show parser
97 1
    info = "display the path of a dependency or internal file"
98 1
    sub = subs.add_parser('show', description=info.capitalize() + '.',
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
99
                          help=info, parents=[debug, project], **shared)
100
    sub.add_argument('name', nargs='*',
101
                     help="display the path of this dependency")
102 1
    sub.add_argument('-c', '--config', action='store_true',
103
                     help="display the path of the config file")
104
    sub.add_argument('-l', '--log', action='store_true',
105 1
                     help="display the path of the log file")
106
107
    # Edit parser
108 1
    info = "open the configuration file in the default editor"
109 1
    sub = subs.add_parser('edit', description=info.capitalize() + '.',
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
110 1
                          help=info, parents=[debug, project], **shared)
111 1
112 1
    # Parse arguments
113
    namespace = parser.parse_args(args=args)
114
115 1
    # Configure logging
116 1
    common.configure_logging(namespace.verbose)
117 1
118 1
    # Run the program
119
    function, args, kwargs, exit_msg = _get_command(function, namespace)
120 1
    if function is None:
121 1
        parser.print_help()
122 1
        sys.exit(1)
123 1
    _run_command(function, args, kwargs, exit_msg)
124
125
126 1
def _get_command(function, namespace):
127 1
    args = []
128 1
    kwargs = dict(root=namespace.root)
129 1
    exit_msg = ""
130
131 1
    if namespace.command in ['install', 'update']:
132 1
        function = getattr(commands, namespace.command)
133 1
        args = namespace.name
134 1
        kwargs.update(depth=namespace.depth,
135
                      force=namespace.force,
136 1
                      clean=namespace.clean)
137 1
        if namespace.command == 'install':
138 1
            kwargs.update(fetch=namespace.fetch)
139 1
        if namespace.command == 'update':
140 1
            kwargs.update(recurse=namespace.recurse,
141 1
                          lock=namespace.lock)
142 1
        exit_msg = "\n" + "Run again with '--force' to overwrite"
143 1
    elif namespace.command == 'list':
144 1
        function = commands.display
145
        kwargs.update(dict(depth=namespace.depth,
146 1
                           allow_dirty=namespace.allow_dirty))
147
    elif namespace.command == 'lock':
148
        function = getattr(commands, namespace.command)
149 1
        args = namespace.name
150 1
    elif namespace.command == 'uninstall':
151 1
        function = commands.delete
152 1
        kwargs.update(force=namespace.force)
153 1
        exit_msg = "\n" + "Run again with '--force' to ignore"
154 1
    elif namespace.command == 'show':
155 1
        function = commands.show
156 1
        args = namespace.name
157 1
        if namespace.config:
158 1
            args.append('__config__')
159
        if namespace.log:
160 1
            args.append('__log__')
161 1
    elif namespace.command == 'edit':
162 1
        function = commands.edit
163
164 1
    return function, args, kwargs, exit_msg
165 1
166
167
def _run_command(function, args, kwargs, exit_msg):
168
    success = False
169
    try:
170
        log.debug("Running %s command...", getattr(function, '__name__', 'a'))
171
        success = function(*args, **kwargs)
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
172
    except KeyboardInterrupt:
173
        log.debug("Command canceled")
174
        exit_msg = ""
175
    except RuntimeError as exc:
176
        exit_msg = str(exc) + exit_msg
177
    else:
178
        exit_msg = ""
179
    if success:
180
        log.debug("Command succeeded")
181
    else:
182
        log.debug("Command failed")
183
        sys.exit(exit_msg or 1)
184
185
186
if __name__ == '__main__':  # pragma: no cover (manual test)
187
    main()
188