summaryrefslogtreecommitdiff
path: root/pylint/config/config_initialization.py
blob: 4f7b614eec95f32f4bf9e4a2e29a324f4faef812 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import sys
from glob import glob
from itertools import chain
from pathlib import Path
from typing import TYPE_CHECKING

from pylint import reporters
from pylint.config.config_file_parser import _ConfigurationFileParser
from pylint.config.exceptions import _UnrecognizedOptionError
from pylint.utils import utils

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def _config_initialization(
    linter: PyLinter,
    args_list: list[str],
    reporter: reporters.BaseReporter | reporters.MultiReporter | None = None,
    config_file: None | str | Path = None,
    verbose_mode: bool = False,
) -> list[str]:
    """Parse all available options, read config files and command line arguments and
    set options accordingly.
    """
    config_file = Path(config_file) if config_file else None

    # Set the current module to the configuration file
    # to allow raising messages on the configuration file.
    linter.set_current_module(str(config_file) if config_file else "")

    # Read the configuration file
    config_file_parser = _ConfigurationFileParser(verbose_mode, linter)
    try:
        config_data, config_args = config_file_parser.parse_config_file(
            file_path=config_file
        )
    except OSError as ex:
        print(ex, file=sys.stderr)
        sys.exit(32)

    # Run init hook, if present, before loading plugins
    if "init-hook" in config_data:
        exec(utils._unquote(config_data["init-hook"]))  # pylint: disable=exec-used

    # Load plugins if specified in the config file
    if "load-plugins" in config_data:
        linter.load_plugin_modules(utils._splitstrip(config_data["load-plugins"]))

    unrecognized_options_message = None
    # First we parse any options from a configuration file
    try:
        linter._parse_configuration_file(config_args)
    except _UnrecognizedOptionError as exc:
        unrecognized_options_message = ", ".join(exc.options)

    # Then, if a custom reporter is provided as argument, it may be overridden
    # by file parameters, so we re-set it here. We do this before command line
    # parsing, so it's still overridable by command line options
    if reporter:
        linter.set_reporter(reporter)

    # Set the current module to the command line
    # to allow raising messages on it
    linter.set_current_module("Command line")

    # Now we parse any options from the command line, so they can override
    # the configuration file
    parsed_args_list = linter._parse_command_line_configuration(args_list)

    # Remove the positional arguments separator from the list of arguments if it exists
    try:
        parsed_args_list.remove("--")
    except ValueError:
        pass

    # Check if there are any options that we do not recognize
    unrecognized_options: list[str] = []
    for opt in parsed_args_list:
        if opt.startswith("--"):
            unrecognized_options.append(opt[2:])
        elif opt.startswith("-"):
            unrecognized_options.append(opt[1:])
    if unrecognized_options:
        msg = ", ".join(unrecognized_options)
        try:
            linter._arg_parser.error(f"Unrecognized option found: {msg}")
        except SystemExit:
            sys.exit(32)

    # Now that config file and command line options have been loaded
    # with all disables, it is safe to emit messages
    if unrecognized_options_message is not None:
        linter.set_current_module(str(config_file) if config_file else "")
        linter.add_message(
            "unrecognized-option", args=unrecognized_options_message, line=0
        )

    linter._emit_stashed_messages()

    # Set the current module to configuration as we don't know where
    # the --load-plugins key is coming from
    linter.set_current_module("Command line or configuration file")

    # We have loaded configuration from config file and command line. Now, we can
    # load plugin specific configuration.
    linter.load_plugin_configuration()

    # Now that plugins are loaded, get list of all fail_on messages, and
    # enable them
    linter.enable_fail_on_messages()

    linter._parse_error_mode()

    # Link the base Namespace object on the current directory
    linter._directory_namespaces[Path(".").resolve()] = (linter.config, {})

    # parsed_args_list should now only be a list of inputs to lint.
    # All other options have been removed from the list.
    return list(
        chain.from_iterable(
            # NOTE: 'or [arg]' is needed in the case the input file or directory does
            # not exist and 'glob(arg)' cannot find anything. Without this we would
            # not be able to output the fatal import error for this module later on,
            # as it would get silently ignored.
            glob(arg, recursive=True) or [arg]
            for arg in parsed_args_list
        )
    )