1# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
2# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
3
4
5import optparse  # pylint: disable=deprecated-module
6from typing import Any, Dict, Tuple
7
8from pylint.config.option import _validate
9
10
11class UnsupportedAction(Exception):
12    """raised by set_option when it doesn't know what to do for an action"""
13
14
15class OptionsProviderMixIn:
16    """Mixin to provide options to an OptionsManager"""
17
18    # those attributes should be overridden
19    priority = -1
20    name = "default"
21    options: Tuple[Tuple[str, Dict[str, Any]], ...] = ()
22    level = 0
23
24    def __init__(self):
25        self.config = optparse.Values()
26        self.load_defaults()
27
28    def load_defaults(self):
29        """initialize the provider using default values"""
30        for opt, optdict in self.options:
31            action = optdict.get("action")
32            if action != "callback":
33                # callback action have no default
34                if optdict is None:
35                    optdict = self.get_option_def(opt)
36                default = optdict.get("default")
37                self.set_option(opt, default, action, optdict)
38
39    def option_attrname(self, opt, optdict=None):
40        """get the config attribute corresponding to opt"""
41        if optdict is None:
42            optdict = self.get_option_def(opt)
43        return optdict.get("dest", opt.replace("-", "_"))
44
45    def option_value(self, opt):
46        """get the current value for the given option"""
47        return getattr(self.config, self.option_attrname(opt), None)
48
49    def set_option(self, optname, value, action=None, optdict=None):
50        """method called to set an option (registered in the options list)"""
51        if optdict is None:
52            optdict = self.get_option_def(optname)
53        if value is not None:
54            value = _validate(value, optdict, optname)
55        if action is None:
56            action = optdict.get("action", "store")
57        if action == "store":
58            setattr(self.config, self.option_attrname(optname, optdict), value)
59        elif action in {"store_true", "count"}:
60            setattr(self.config, self.option_attrname(optname, optdict), 0)
61        elif action == "store_false":
62            setattr(self.config, self.option_attrname(optname, optdict), 1)
63        elif action == "append":
64            optname = self.option_attrname(optname, optdict)
65            _list = getattr(self.config, optname, None)
66            if _list is None:
67                if isinstance(value, (list, tuple)):
68                    _list = value
69                elif value is not None:
70                    _list = []
71                    _list.append(value)
72                setattr(self.config, optname, _list)
73            elif isinstance(_list, tuple):
74                setattr(self.config, optname, _list + (value,))
75            else:
76                _list.append(value)
77        elif action == "callback":
78            optdict["callback"](None, optname, value, None)
79        else:
80            raise UnsupportedAction(action)
81
82    def get_option_def(self, opt):
83        """return the dictionary defining an option given its name"""
84        assert self.options
85        for option in self.options:
86            if option[0] == opt:
87                return option[1]
88        raise optparse.OptionError(
89            f"no such option {opt} in section {self.name!r}", opt
90        )
91
92    def options_by_section(self):
93        """return an iterator on options grouped by section
94
95        (section, [list of (optname, optdict, optvalue)])
96        """
97        sections = {}
98        for optname, optdict in self.options:
99            sections.setdefault(optdict.get("group"), []).append(
100                (optname, optdict, self.option_value(optname))
101            )
102        if None in sections:
103            yield None, sections.pop(None)
104        for section, options in sorted(sections.items()):
105            yield section.upper(), options
106
107    def options_and_values(self, options=None):
108        if options is None:
109            options = self.options
110        for optname, optdict in options:
111            yield (optname, optdict, self.option_value(optname))
112