Failed Conditions
Pull Request — master (#1511)
by Abdeali
01:34
created

coalib.settings.get_config_directory()   B

Complexity

Conditions 5

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 5
dl 0
loc 10
rs 8.5454
1
import os
2
import sys
3
4
from coalib.settings.Setting import Setting
5
from coalib.misc import Constants
6
from coalib.output.ConfWriter import ConfWriter
7
from coalib.output.printers.LOG_LEVEL import LOG_LEVEL
8
from coalib.parsing.CliParsing import parse_cli
9
from coalib.parsing.ConfParser import ConfParser
10
from coalib.settings.Section import Section
11
from coalib.settings.SectionFilling import fill_settings
12
13
14
def merge_section_dicts(lower, higher):
15
    """
16
    Merges the section dictionaries. The values of higher will take
17
    precedence over the ones of lower. Lower will hold the modified dict in
18
    the end.
19
20
    :param lower:  A section.
21
    :param higher: A section which values will take precedence over the ones
22
                   from the other.
23
    :return:       The merged dict.
24
    """
25
    for name in higher:
26
        if name in lower:
27
            lower[name].update(higher[name], ignore_defaults=True)
28
        else:
29
            # no deep copy needed
30
            lower[name] = higher[name]
31
32
    return lower
33
34
35
def load_config_file(filename, log_printer, silent=False):
36
    """
37
    Loads sections from a config file. Prints an appropriate warning if
38
    it doesn't exist and returns a section dict containing an empty
39
    default section in that case.
40
41
    It assumes that the cli_sections are available.
42
43
    :param filename:    The file to load settings from.
44
    :param log_printer: The log printer to log the warning/error to (in case).
45
    :param silent:      Whether or not to warn the user/exit if the file
46
                        doesn't exist.
47
    :raises SystemExit: Exits when given filename is invalid and is not the
48
                        default coafile. Only raised when `silent` is `False`.
49
    """
50
    filename = os.path.abspath(filename)
51
52
    try:
53
        return ConfParser().parse(filename)
54
    except FileNotFoundError:
55
        if not silent:
56
            if os.path.basename(filename) == Constants.default_coafile:
57
                log_printer.warn("The default coafile " +
58
                                 repr(Constants.default_coafile) + " was not "
59
                                 "found. Ignoring it.")
60
            else:
61
                log_printer.err("The requested coafile " + repr(filename) +
62
                                " does not exist.")
63
                sys.exit(2)
64
65
        return {"default": Section("default")}
66
67
68
def save_sections(sections):
69
    """
70
    Saves the given sections if they are to be saved.
71
72
    :param sections: A section dict.
73
    """
74
    default_section = sections["default"]
75
    try:
76
        if bool(default_section.get("save", "false")):
77
            conf_writer = ConfWriter(
78
                str(default_section.get("config", Constants.default_coafile)))
79
        else:
80
            return
81
    except ValueError:
82
        conf_writer = ConfWriter(str(default_section.get("save", ".coafile")))
83
84
    conf_writer.write_sections(sections)
85
    conf_writer.close()
86
87
88
def warn_nonexistent_targets(targets, sections, log_printer):
89
    """
90
    Prints out a warning on the given log printer for all targets that are
91
    not existent within the given sections.
92
93
    :param targets:     The targets to check.
94
    :param sections:    The sections to search. (Dict.)
95
    :param log_printer: The log printer to warn to.
96
    """
97
    for target in targets:
98
        if target not in sections:
99
            log_printer.warn(
100
                "The requested section '{section}' is not existent. "
101
                "Thus it cannot be executed.".format(section=target))
102
103
104
def load_configuration(arg_list, log_printer):
105
    """
106
    Parses the CLI args and loads the config file accordingly, taking
107
    default_coafile and the users .coarc into account.
108
109
    :param arg_list:    The list of command line arguments.
110
    :param log_printer: The LogPrinter object for logging.
111
    :return:            A tuple holding (log_printer: LogPrinter, sections:
112
                        dict(str, Section), targets: list(str)). (Types
113
                        indicated after colon.)
114
    """
115
    cli_sections = parse_cli(arg_list=arg_list)
116
117
    if (
118
            bool(cli_sections["default"].get("find_config", "False")) and
119
            str(cli_sections["default"].get("config")) == ""):
120
        cli_sections["default"].add_or_create_setting(
121
            Setting("config", find_user_config(os.getcwd())))
122
123
    targets = []
124
    # We don't want to store targets argument back to file, thus remove it
125
    for item in list(cli_sections["default"].contents.pop("targets", "")):
126
        targets.append(item.lower())
127
128
    default_sections = load_config_file(Constants.system_coafile,
129
                                        log_printer)
130
131
    user_sections = load_config_file(
132
        Constants.user_coafile,
133
        log_printer,
134
        silent=True)
135
136
    default_config = str(default_sections["default"].get("config", ".coafile"))
137
    user_config = str(user_sections["default"].get("config", default_config))
138
    config = os.path.abspath(
139
        str(cli_sections["default"].get("config", user_config)))
140
141
    try:
142
        save = bool(cli_sections["default"].get("save", "False"))
143
    except ValueError:
144
        # A file is deposited for the save parameter, means we want to save but
145
        # to a specific file.
146
        save = True
147
148
    coafile_sections = load_config_file(config, log_printer, silent=save)
149
150
    sections = merge_section_dicts(default_sections, user_sections)
151
152
    sections = merge_section_dicts(sections, coafile_sections)
153
154
    sections = merge_section_dicts(sections, cli_sections)
155
156
    for section in sections:
157
        if section != "default":
158
            sections[section].defaults = sections["default"]
159
160
    str_log_level = str(sections["default"].get("log_level", "")).upper()
161
    log_printer.log_level = LOG_LEVEL.str_dict.get(str_log_level,
162
                                                   LOG_LEVEL.INFO)
163
164
    return sections, targets
165
166
167
def find_user_config(file_path, max_trials=10):
168
    """
169
    Uses the filepath to find the most suitable user config file for the file
170
    by going down one directory at a time and finding config files there.
171
172
    :param file_path:  The path of the file whose user config needs to be found
173
    :param max_trials: The maximum number of directories to go down to.
174
    :return:           The config file's path
175
    """
176
    file_path = os.path.normpath(os.path.abspath(os.path.expanduser(
177
        file_path)))
178
    old_dir = None
179
    base_dir = os.path.dirname(file_path)
180
    home_dir = os.path.expanduser("~")
181
182
    while base_dir != old_dir and old_dir != home_dir and max_trials != 0:
183
        config_file = os.path.join(base_dir, ".coafile")
184
        if os.path.isfile(config_file):
185
            return config_file
186
187
        old_dir = base_dir
188
        base_dir = os.path.dirname(old_dir)
189
        max_trials = max_trials - 1
190
191
    return ""
192
193
194
def get_config_directory(section):
195
    if section is None:
196
        return os.getcwd()
197
    try:
198
        path = str(section["config"])
199
        return path if os.path.isdir(path) else os.path.dirname(path)
200
    except IndexError:
201
        if os.path.isfile(os.path.join(os.getcwd(), '.coafile')):
202
            return os.getcwd()
203
        return None
204
205
206
def gather_configuration(acquire_settings,
207
                         log_printer,
208
                         autoapply=None,
209
                         arg_list=None):
210
    """
211
    Loads all configuration files, retrieves bears and all needed
212
    settings, saves back if needed and warns about non-existent targets.
213
214
    This function:
215
    - Reads and merges all settings in sections from
216
        - Default config
217
        - User config
218
        - Configuration file
219
        - CLI
220
    - Collects all the bears
221
    - Fills up all needed settings
222
    - Writes back the new sections to the configuration file if needed
223
    - Gives all information back to caller
224
225
    :param acquire_settings: The method to use for requesting settings. It will
226
                             get a parameter which is a dictionary with the
227
                             settings name as key and a list containing a
228
                             description in [0] and the names of the bears
229
                             who need this setting in all following indexes.
230
    :param log_printer:      The log printer to use for logging. The log level
231
                             will be adjusted to the one given by the section.
232
    :param autoapply:        Set whether to autoapply patches. This is
233
                             overridable via any configuration file/CLI.
234
    :param arg_list:         CLI args to use
235
    :return:                 A tuple with the following contents:
236
                              * A dictionary with the sections
237
                              * Dictionary of list of local bears for each
238
                                section
239
                              * Dictionary of list of global bears for each
240
                                section
241
                              * The targets list
242
    """
243
    # Note: arg_list can also be []. Hence we cannot use
244
    # `arg_list = arg_list or default_list`
245
    arg_list = sys.argv[1:] if arg_list is None else arg_list
246
    sections, targets = load_configuration(arg_list, log_printer)
247
    local_bears, global_bears = fill_settings(sections,
248
                                              acquire_settings,
249
                                              log_printer)
250
    save_sections(sections)
251
    warn_nonexistent_targets(targets, sections, log_printer)
252
253
    if not autoapply is None:
254
        if not autoapply and 'autoapply' not in sections['default']:
255
            sections['default']['autoapply'] = "False"
256
257
    return (sections,
258
            local_bears,
259
            global_bears,
260
            targets)
261