Completed
Pull Request — master (#53)
by Jace
02:11
created

mine._restart_daemon()   A

Complexity

Conditions 4

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

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