Completed
Push — develop ( 3dabc3...f1379d )
by Jace
03:04 queued 17s
created

_run_command()   B

Complexity

Conditions 6

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
dl 0
loc 24
ccs 21
cts 21
cp 1
crap 6
rs 7.6129
c 0
b 0
f 0
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, exceptions, commands
0 ignored issues
show
Bug introduced by
The name commands does not seem to exist in module gitman.
Loading history...
11
12 1
log = logging.getLogger(__name__)
0 ignored issues
show
Coding Style Naming introduced by
The name log does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
13
14
15 1
def main(args=None, function=None):  # pylint: disable=too-many-statements
0 ignored issues
show
introduced by
Locally disabling too-many-statements (R0915)
Loading history...
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,
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...
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 configuration file for the project"
47 1
    sub = subs.add_parser('init', 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...
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() + '.',
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...
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() + '.',
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...
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="update all nested dependencies, recursively")
69 1
    group = sub.add_mutually_exclusive_group()
70 1
    group.add_argument('-l', '--lock',
71
                       action='store_true', dest='lock', default=None,
72
                       help="enable recording of versions for later reinstall")
73 1
    group.add_argument('-L', '--no-lock',
74
                       action='store_false', dest='lock', default=None,
75
                       help="disable recording of versions for later reinstall")
76
77
    # List parser
78 1
    info = "display the current version of each dependency"
79 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...
80
                          help=info, parents=[debug, project, depth], **shared)
81 1
    sub.add_argument('-D', '--no-dirty', action='store_false',
82
                     dest='allow_dirty',
83
                     help="fail if a source has uncommitted changes")
84
85
    # Lock parser
86 1
    info = "lock the current version of each dependency"
87 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...
88
                          help=info, parents=[debug, project], **shared)
89 1
    sub.add_argument('name', nargs='*',
90
                     help="list of dependency names to lock")
91
92
    # Uninstall parser
93 1
    info = "delete all installed dependencies"
94 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...
95
                          help=info, parents=[debug, project], **shared)
96 1
    sub.add_argument('-f', '--force', action='store_true',
97
                     help="delete uncommitted changes in dependencies")
98
99
    # Show parser
100 1
    info = "display the path of a dependency or internal file"
101 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...
102
                          help=info, parents=[debug, project], **shared)
103 1
    sub.add_argument('name', nargs='*',
104
                     help="display the path of this dependency")
105 1
    sub.add_argument('-c', '--config', action='store_true',
106
                     help="display the path of the config file")
107 1
    sub.add_argument('-l', '--log', action='store_true',
108
                     help="display the path of the log file")
109
110
    # Edit parser
111 1
    info = "open the configuration file in the default editor"
112 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...
113
                          help=info, parents=[debug, project], **shared)
114
115
    # Parse arguments
116 1
    namespace = parser.parse_args(args=args)
117
118
    # Configure logging
119 1
    common.configure_logging(namespace.verbose)
120
121
    # Run the program
122 1
    function, args, kwargs = _get_command(function, namespace)
123 1
    if function:
124 1
        _run_command(function, args, kwargs)
125
    else:
126 1
        parser.print_help()
127 1
        sys.exit(1)
128
129
130 1
def _get_command(function, namespace):  # pylint: disable=too-many-statements
0 ignored issues
show
introduced by
Locally disabling too-many-statements (R0915)
Loading history...
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
131 1
    args = []
132 1
    kwargs = {}
133
134 1
    if namespace.command == 'init':
135 1
        function = commands.init
136
137 1
    elif namespace.command in ['install', 'update']:
138 1
        function = getattr(commands, namespace.command)
139 1
        args = namespace.name
140 1
        kwargs.update(root=namespace.root,
141
                      depth=namespace.depth,
142
                      force=namespace.force,
143
                      clean=namespace.clean)
144 1
        if namespace.command == 'install':
145 1
            kwargs.update(fetch=namespace.fetch)
146 1
        if namespace.command == 'update':
147 1
            kwargs.update(recurse=namespace.recurse,
148
                          lock=namespace.lock)
149
150 1
    elif namespace.command == 'list':
151 1
        function = commands.display
152 1
        kwargs.update(root=namespace.root,
153
                      depth=namespace.depth,
154
                      allow_dirty=namespace.allow_dirty)
155
156 1
    elif namespace.command == 'lock':
157 1
        function = getattr(commands, namespace.command)
158 1
        args = namespace.name
159 1
        kwargs.update(root=namespace.root)
160
161 1
    elif namespace.command == 'uninstall':
162 1
        function = commands.delete
163 1
        kwargs.update(root=namespace.root,
164
                      force=namespace.force)
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):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
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)
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...
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):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
209
    # TODO: require level=, evaluate all calls to dedent()
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
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