blob: cf53adabaa22d6e783e2c4dc1213bc14487a563d [file] [log] [blame]
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/master/LICENSE
import copy
import optparse # pylint: disable=deprecated-module
import re
from pylint import utils
# pylint: disable=unused-argument
def _csv_validator(_, name, value):
return utils._check_csv(value)
# pylint: disable=unused-argument
def _regexp_validator(_, name, value):
if hasattr(value, "pattern"):
return value
return re.compile(value)
# pylint: disable=unused-argument
def _regexp_csv_validator(_, name, value):
return [_regexp_validator(_, name, val) for val in _csv_validator(_, name, value)]
def _choice_validator(choices, name, value):
if value not in choices:
msg = "option %s: invalid value: %r, should be in %s"
raise optparse.OptionValueError(msg % (name, value, choices))
return value
def _yn_validator(opt, _, value):
if isinstance(value, int):
return bool(value)
if value in ("y", "yes"):
return True
if value in ("n", "no"):
return False
msg = "option %s: invalid yn value %r, should be in (y, yes, n, no)"
raise optparse.OptionValueError(msg % (opt, value))
def _multiple_choice_validator(choices, name, value):
values = utils._check_csv(value)
for csv_value in values:
if csv_value not in choices:
msg = "option %s: invalid value: %r, should be in %s"
raise optparse.OptionValueError(msg % (name, csv_value, choices))
return values
def _non_empty_string_validator(opt, _, value):
if not value:
msg = "indent string can't be empty."
raise optparse.OptionValueError(msg)
return utils._unquote(value)
def _multiple_choices_validating_option(opt, name, value):
return _multiple_choice_validator(opt.choices, name, value)
def _py_version_validator(_, name, value):
if not isinstance(value, tuple):
try:
value = tuple(int(val) for val in value.split("."))
except (ValueError, AttributeError):
raise optparse.OptionValueError(f"Invalid format for {name}") from None
return value
VALIDATORS = {
"string": utils._unquote,
"int": int,
"float": float,
"regexp": re.compile,
"regexp_csv": _regexp_csv_validator,
"csv": _csv_validator,
"yn": _yn_validator,
"choice": lambda opt, name, value: _choice_validator(opt["choices"], name, value),
"multiple_choice": lambda opt, name, value: _multiple_choice_validator(
opt["choices"], name, value
),
"non_empty_string": _non_empty_string_validator,
"py_version": _py_version_validator,
}
def _call_validator(opttype, optdict, option, value):
if opttype not in VALIDATORS:
raise Exception('Unsupported type "%s"' % opttype)
try:
return VALIDATORS[opttype](optdict, option, value)
except TypeError:
try:
return VALIDATORS[opttype](value)
except Exception as e:
raise optparse.OptionValueError(
f"{option} value ({value!r}) should be of type {opttype}"
) from e
def _validate(value, optdict, name=""):
"""return a validated value for an option according to its type
optional argument name is only used for error message formatting
"""
try:
_type = optdict["type"]
except KeyError:
return value
return _call_validator(_type, optdict, name, value)
# pylint: disable=no-member
class Option(optparse.Option):
TYPES = optparse.Option.TYPES + (
"regexp",
"regexp_csv",
"csv",
"yn",
"multiple_choice",
"non_empty_string",
"py_version",
)
ATTRS = optparse.Option.ATTRS + ["hide", "level"]
TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
TYPE_CHECKER["regexp"] = _regexp_validator
TYPE_CHECKER["regexp_csv"] = _regexp_csv_validator
TYPE_CHECKER["csv"] = _csv_validator
TYPE_CHECKER["yn"] = _yn_validator
TYPE_CHECKER["multiple_choice"] = _multiple_choices_validating_option
TYPE_CHECKER["non_empty_string"] = _non_empty_string_validator
TYPE_CHECKER["py_version"] = _py_version_validator
def __init__(self, *opts, **attrs):
optparse.Option.__init__(self, *opts, **attrs)
if hasattr(self, "hide") and self.hide:
self.help = optparse.SUPPRESS_HELP
def _check_choice(self):
if self.type in ("choice", "multiple_choice"):
if self.choices is None:
raise optparse.OptionError(
"must supply a list of choices for type 'choice'", self
)
if not isinstance(self.choices, (tuple, list)):
raise optparse.OptionError(
"choices must be a list of strings ('%s' supplied)"
% str(type(self.choices)).split("'")[1],
self,
)
elif self.choices is not None:
raise optparse.OptionError(
"must not supply choices for type %r" % self.type, self
)
# pylint: disable=unsupported-assignment-operation
optparse.Option.CHECK_METHODS[2] = _check_choice # type: ignore
def process(self, opt, value, values, parser):
# First, convert the value(s) to the right type. Howl if any
# value(s) are bogus.
value = self.convert_value(opt, value)
if self.type == "named":
existent = getattr(values, self.dest)
if existent:
existent.update(value)
value = existent
# And then take whatever action is expected of us.
# This is a separate method to make life easier for
# subclasses to add new actions.
return self.take_action(self.action, self.dest, opt, value, values, parser)