Completed
Push — master ( a9fd17...8a9714 )
by Jace
7s
created

main()   F

Complexity

Conditions 9

Size

Total Lines 78

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 45
CRAP Score 9.0008

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
c 1
b 0
f 0
dl 0
loc 78
ccs 45
cts 46
cp 0.9783
crap 9.0008
rs 3.8611

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 python
2
3 1
"""Command-line interface."""
4
5 1
import sys
6 1
import time
7 1
import argparse
8 1
import subprocess
9 1
import logging
10
11 1
import yorm
0 ignored issues
show
Configuration introduced by
The import yorm could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
12
13 1
from . import CLI, VERSION, DESCRIPTION
14 1
from . import common
15 1
from . import services
16 1
from .data import Data
17 1
from .computer import PLACEHOLDER
18 1
from .application import Application
19 1
from .manager import get_manager
20
21
22 1
log = logging.getLogger(__name__)
23 1
daemon = Application(CLI, filename=CLI)
24
25
26 1
def main(args=None):
27
    """Process command-line arguments and run the program."""
28
29
    # Shared options
30 1
    debug = argparse.ArgumentParser(add_help=False)
31 1
    debug.add_argument('-V', '--version', action='version', version=VERSION)
32 1
    group = debug.add_mutually_exclusive_group()
33 1
    group.add_argument('-v', '--verbose', action='count', default=0,
34
                       help="enable verbose logging")
35 1
    group.add_argument('-q', '--quiet', action='store_const', const=-1,
36
                       dest='verbose', help="only display errors and prompts")
37 1
    shared = {'formatter_class': common.HelpFormatter,
38
              'parents': [debug]}
39
40
    # Build 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
                                     **shared)
43 1
    parser.add_argument('-d', '--daemon', metavar='DELAY', nargs='?', const=300,
44
                        type=int, help="run continuously with delay [seconds]")
45 1
    parser.add_argument('-f', '--file', help="custom settings file path")
46 1
    subs = parser.add_subparsers(help="", dest='command', metavar="<command>")
47
48
    # Build switch parser
49 1
    info = "start applications on another computer"
50 1
    sub = subs.add_parser('switch', 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...
51
                          help=info, **shared)
52 1
    sub.add_argument('name', nargs='?',
53
                     help="computer to queue for launch (default: current)")
54
55
    # Build close parser
56 1
    info = "close applications on this computer"
57 1
    sub = subs.add_parser('close', 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...
58
                          help=info, **shared)
59
60
    # Build edit parser
61 1
    info = "launch the settings file for editing"
62 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...
63
                          help=info, **shared)
64
65
    # Build clean parser
66 1
    info = "display and delete conflicted files"
67 1
    sub = subs.add_parser('clean', 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...
68
                          help=info, **shared)
69 1
    sub.add_argument('-f', '--force', action='store_true',
70
                     help="actually delete the conflicted files")
71
72
    # Parse arguments
73 1
    args = parser.parse_args(args=args)
74 1
    kwargs = {'delay': args.daemon}
75 1
    if args.command == 'switch':
76 1
        kwargs['switch'] = args.name if args.name else True
77 1
    elif args.command == 'close':
78
        kwargs['switch'] = False
79 1
    elif args.command == 'edit':
80 1
        kwargs['edit'] = True
81 1
    elif args.command == 'clean':
82 1
        kwargs['delete'] = True
83 1
        kwargs['force'] = args.force
84
85
    # Configure logging
86 1
    common.configure_logging(args.verbose)
87
88
    # Run the program
89 1
    try:
90 1
        log.debug("Running main command...")
91 1
        success = run(path=args.file, **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...
92 1
    except KeyboardInterrupt:
93 1
        msg = "command canceled"
94 1
        if common.verbosity == common.MAX_VERBOSITY:
95 1
            log.exception(msg)
96
        else:
97 1
            log.debug(msg)
98 1
        success = False
99 1
    if success:
100 1
        log.debug("Command succeeded")
101
    else:
102 1
        log.debug("Command failed")
103 1
        sys.exit(1)
104
105
106 1
def run(path=None, cleanup=True, delay=None,
107
        switch=None,
108
        edit=False,
109
        delete=False, force=False):
110
    """Run the program.
111
112
    :param path: custom settings file path
113
    :param cleanup: remove unused items from the config
114
    :param delay: number of seconds to delay before repeating
115
116
    :param switch: computer name to queue for launch
117
118
    :param edit: launch the configuration file for editing
119
120
    :param delete: attempt to delete conflicted files
121
    :param force: actually delete conflicted files
122
123
    """
124 1
    manager = get_manager()
125 1
    root = services.find_root()
126 1
    path = path or services.find_config_path(root=root)
127
128 1
    data = Data()
129 1
    yorm.sync(data, path)
130
131 1
    config = data.config
132 1
    status = data.status
133
134 1
    log.info("Identifying current computer...")
135 1
    computer = config.computers.get_current()
136 1
    log.info("Current computer: %s", computer)
137
138 1
    if edit:
139
        return manager.launch(path)
140 1
    if delete:
141
        return services.delete_conflicts(root, force=force)
142 1
    if log.getEffectiveLevel() >= logging.WARNING:
143
        print("Updating application state...")
144
145 1
    if switch is True:
146
        switch = computer
147 1
    elif switch is False:
148
        switch = PLACEHOLDER
149 1
    elif switch:
150
        switch = config.computers.match(switch)
151 1
    if switch:
152
        data.queue(config, status, switch)
153
154 1
    while True:
155 1
        data.launch(config, status, computer, manager)
156 1
        data.update(config, status, computer, manager)
157
158 1
        if delay is None:
159 1
            break
160
161
        log.info("Delaying %s seconds for files to sync...", delay)
162
        time.sleep(delay)
163
164
        step = 1
165
        elapsed = 0
166
        log.info("Waiting %s seconds for status changes...", delay)
167
        while elapsed < delay and not data.modified:
168
            time.sleep(step)
169
            elapsed += step
170
171
        services.delete_conflicts(root, config_only=True, force=True)
172
173 1
    if cleanup:
174 1
        data.clean(config, status)
175
176 1
    if delay is None:
177 1
        return _restart_daemon(manager)
178
179
    return True
180
181
182 1
def _restart_daemon(manager):
183 1
    cmd = "nohup {} --daemon --verbose >> /tmp/mine.log 2>&1 &".format(CLI)
184 1
    if daemon and not manager.is_running(daemon):
185 1
        log.warning("Daemon is not running, attempting to restart...")
186
187 1
        log.info("$ %s", cmd)
188 1
        subprocess.call(cmd, shell=True)
189 1
        if manager.is_running(daemon):
190 1
            return True
191
192 1
        log.error("Manually start daemon: %s", cmd)
193 1
        return False
194
195 1
    return True
196
197
198
if __name__ == '__main__':  # pragma: no cover (manual test)
199
    main()
200