| Viewing file:  arguments_manager.py (31.95 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
 # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
 
 """Arguments manager class used to handle command-line arguments and options."""
 
 from __future__ import annotations
 
 import argparse
 import configparser
 import copy
 import optparse  # pylint: disable=deprecated-module
 import os
 import re
 import sys
 import textwrap
 import warnings
 from collections import OrderedDict
 from collections.abc import Sequence
 from pathlib import Path
 from typing import TYPE_CHECKING, Any, TextIO, Union
 
 import tomlkit
 
 from pylint import utils
 from pylint.config.argument import (
 _Argument,
 _CallableArgument,
 _ExtendArgument,
 _StoreArgument,
 _StoreNewNamesArgument,
 _StoreOldNamesArgument,
 _StoreTrueArgument,
 )
 from pylint.config.exceptions import (
 UnrecognizedArgumentAction,
 _UnrecognizedOptionError,
 )
 from pylint.config.help_formatter import _HelpFormatter
 from pylint.config.option import Option
 from pylint.config.option_parser import OptionParser  # type: ignore[attr-defined]
 from pylint.config.options_provider_mixin import (  # type: ignore[attr-defined]
 OptionsProviderMixIn,
 )
 from pylint.config.utils import _convert_option_to_argument, _parse_rich_type_value
 from pylint.constants import MAIN_CHECKER_NAME
 from pylint.typing import DirectoryNamespaceDict, OptionDict
 
 if sys.version_info >= (3, 11):
 import tomllib
 else:
 import tomli as tomllib
 
 
 if TYPE_CHECKING:
 from pylint.config.arguments_provider import _ArgumentsProvider
 
 ConfigProvider = Union["_ArgumentsProvider", OptionsProviderMixIn]
 
 
 # pylint: disable-next=too-many-instance-attributes
 class _ArgumentsManager:
 """Arguments manager class used to handle command-line arguments and options."""
 
 def __init__(
 self, prog: str, usage: str | None = None, description: str | None = None
 ) -> None:
 self._config = argparse.Namespace()
 """Namespace for all options."""
 
 self._base_config = self._config
 """Fall back Namespace object created during initialization.
 
 This is necessary for the per-directory configuration support. Whenever we
 fail to match a file with a directory we fall back to the Namespace object
 created during initialization.
 """
 
 self._arg_parser = argparse.ArgumentParser(
 prog=prog,
 usage=usage or "%(prog)s [options]",
 description=description,
 formatter_class=_HelpFormatter,
 # Needed to let 'pylint-config' overwrite the -h command
 conflict_handler="resolve",
 )
 """The command line argument parser."""
 
 self._argument_groups_dict: dict[str, argparse._ArgumentGroup] = {}
 """Dictionary of all the argument groups."""
 
 self._option_dicts: dict[str, OptionDict] = {}
 """All option dictionaries that have been registered."""
 
 self._directory_namespaces: DirectoryNamespaceDict = {}
 """Mapping of directories and their respective namespace objects."""
 
 # TODO: 3.0: Remove deprecated attributes introduced to keep API
 # parity with optparse. Until '_maxlevel'
 with warnings.catch_warnings():
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 self.reset_parsers(usage or "")
 # list of registered options providers
 self._options_providers: list[ConfigProvider] = []
 # dictionary associating option name to checker
 self._all_options: OrderedDict[str, ConfigProvider] = OrderedDict()
 self._short_options: dict[str, str] = {}
 self._nocallback_options: dict[ConfigProvider, str] = {}
 self._mygroups: dict[str, optparse.OptionGroup] = {}
 # verbosity
 self._maxlevel: int = 0
 
 @property
 def config(self) -> argparse.Namespace:
 """Namespace for all options."""
 return self._config
 
 @config.setter
 def config(self, value: argparse.Namespace) -> None:
 self._config = value
 
 @property
 def options_providers(self) -> list[ConfigProvider]:
 # TODO: 3.0: Remove deprecated attribute.
 warnings.warn(
 "options_providers has been deprecated. It will be removed in pylint 3.0.",
 DeprecationWarning,
 stacklevel=2,
 )
 return self._options_providers
 
 @options_providers.setter
 def options_providers(self, value: list[ConfigProvider]) -> None:
 warnings.warn(
 "Setting options_providers has been deprecated. It will be removed in pylint 3.0.",
 DeprecationWarning,
 stacklevel=2,
 )
 self._options_providers = value
 
 def _register_options_provider(self, provider: _ArgumentsProvider) -> None:
 """Register an options provider and load its defaults."""
 for opt, optdict in provider.options:
 self._option_dicts[opt] = optdict
 argument = _convert_option_to_argument(opt, optdict)
 section = argument.section or provider.name.capitalize()
 
 section_desc = provider.option_groups_descs.get(section, None)
 
 # We exclude main since its docstring comes from PyLinter
 if provider.name != MAIN_CHECKER_NAME and provider.__doc__:
 section_desc = provider.__doc__.split("\n\n")[0]
 
 self._add_arguments_to_parser(section, section_desc, argument)
 
 self._load_default_argument_values()
 
 def _add_arguments_to_parser(
 self, section: str, section_desc: str | None, argument: _Argument
 ) -> None:
 """Add an argument to the correct argument section/group."""
 try:
 section_group = self._argument_groups_dict[section]
 except KeyError:
 if section_desc:
 section_group = self._arg_parser.add_argument_group(
 section, section_desc
 )
 else:
 section_group = self._arg_parser.add_argument_group(title=section)
 self._argument_groups_dict[section] = section_group
 self._add_parser_option(section_group, argument)
 
 @staticmethod
 def _add_parser_option(
 section_group: argparse._ArgumentGroup, argument: _Argument
 ) -> None:
 """Add an argument."""
 if isinstance(argument, _StoreArgument):
 section_group.add_argument(
 *argument.flags,
 action=argument.action,
 default=argument.default,
 type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
 help=argument.help,
 metavar=argument.metavar,
 choices=argument.choices,
 )
 elif isinstance(argument, _StoreOldNamesArgument):
 section_group.add_argument(
 *argument.flags,
 **argument.kwargs,
 action=argument.action,
 default=argument.default,
 type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
 help=argument.help,
 metavar=argument.metavar,
 choices=argument.choices,
 )
 # We add the old name as hidden option to make it's default value gets loaded when
 # argparse initializes all options from the checker
 assert argument.kwargs["old_names"]
 for old_name in argument.kwargs["old_names"]:
 section_group.add_argument(
 f"--{old_name}",
 action="store",
 default=argument.default,
 type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
 help=argparse.SUPPRESS,
 metavar=argument.metavar,
 choices=argument.choices,
 )
 elif isinstance(argument, _StoreNewNamesArgument):
 section_group.add_argument(
 *argument.flags,
 **argument.kwargs,
 action=argument.action,
 default=argument.default,
 type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
 help=argument.help,
 metavar=argument.metavar,
 choices=argument.choices,
 )
 elif isinstance(argument, _StoreTrueArgument):
 section_group.add_argument(
 *argument.flags,
 action=argument.action,
 default=argument.default,
 help=argument.help,
 )
 elif isinstance(argument, _CallableArgument):
 section_group.add_argument(
 *argument.flags,
 **argument.kwargs,
 action=argument.action,
 help=argument.help,
 metavar=argument.metavar,
 )
 elif isinstance(argument, _ExtendArgument):
 section_group.add_argument(
 *argument.flags,
 action=argument.action,
 default=argument.default,
 type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
 help=argument.help,
 metavar=argument.metavar,
 choices=argument.choices,
 dest=argument.dest,
 )
 else:
 raise UnrecognizedArgumentAction
 
 def _load_default_argument_values(self) -> None:
 """Loads the default values of all registered options."""
 self.config = self._arg_parser.parse_args([], self.config)
 
 def _parse_configuration_file(self, arguments: list[str]) -> None:
 """Parse the arguments found in a configuration file into the namespace."""
 try:
 self.config, parsed_args = self._arg_parser.parse_known_args(
 arguments, self.config
 )
 except SystemExit:
 sys.exit(32)
 unrecognized_options: list[str] = []
 for opt in parsed_args:
 if opt.startswith("--"):
 unrecognized_options.append(opt[2:])
 if unrecognized_options:
 raise _UnrecognizedOptionError(options=unrecognized_options)
 
 def _parse_command_line_configuration(
 self, arguments: Sequence[str] | None = None
 ) -> list[str]:
 """Parse the arguments found on the command line into the namespace."""
 arguments = sys.argv[1:] if arguments is None else arguments
 
 self.config, parsed_args = self._arg_parser.parse_known_args(
 arguments, self.config
 )
 
 return parsed_args
 
 def reset_parsers(self, usage: str = "") -> None:  # pragma: no cover
 """DEPRECATED."""
 warnings.warn(
 "reset_parsers has been deprecated. Parsers should be instantiated "
 "once during initialization and do not need to be reset.",
 DeprecationWarning,
 stacklevel=2,
 )
 # configuration file parser
 self.cfgfile_parser = configparser.ConfigParser(
 inline_comment_prefixes=("#", ";")
 )
 # command line parser
 self.cmdline_parser = OptionParser(Option, usage=usage)
 self.cmdline_parser.options_manager = self
 self._optik_option_attrs = set(self.cmdline_parser.option_class.ATTRS)
 
 def register_options_provider(
 self, provider: ConfigProvider, own_group: bool = True
 ) -> None:  # pragma: no cover
 """DEPRECATED: Register an options provider."""
 warnings.warn(
 "register_options_provider has been deprecated. Options providers and "
 "arguments providers should be registered by initializing ArgumentsProvider. "
 "This automatically registers the provider on the ArgumentsManager.",
 DeprecationWarning,
 stacklevel=2,
 )
 self.options_providers.append(provider)
 non_group_spec_options = [
 option for option in provider.options if "group" not in option[1]
 ]
 groups = getattr(provider, "option_groups", ())
 if own_group and non_group_spec_options:
 with warnings.catch_warnings():
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 self.add_option_group(
 provider.name.upper(),
 provider.__doc__,
 non_group_spec_options,
 provider,
 )
 else:
 for opt, optdict in non_group_spec_options:
 with warnings.catch_warnings():
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 self.add_optik_option(provider, self.cmdline_parser, opt, optdict)
 for gname, gdoc in groups:
 gname = gname.upper()
 goptions = [
 option
 for option in provider.options
 if option[1].get("group", "").upper() == gname  # type: ignore[union-attr]
 ]
 with warnings.catch_warnings():
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 self.add_option_group(gname, gdoc, goptions, provider)
 
 def add_option_group(
 self,
 group_name: str,
 _: str | None,
 options: list[tuple[str, OptionDict]],
 provider: ConfigProvider,
 ) -> None:  # pragma: no cover
 """DEPRECATED."""
 warnings.warn(
 "add_option_group has been deprecated. Option groups should be "
 "registered by initializing ArgumentsProvider. "
 "This automatically registers the group on the ArgumentsManager.",
 DeprecationWarning,
 stacklevel=2,
 )
 # add option group to the command line parser
 if group_name in self._mygroups:
 group = self._mygroups[group_name]
 else:
 group = optparse.OptionGroup(
 self.cmdline_parser, title=group_name.capitalize()
 )
 self.cmdline_parser.add_option_group(group)
 self._mygroups[group_name] = group
 # add section to the config file
 if (
 group_name != "DEFAULT"
 and group_name not in self.cfgfile_parser._sections  # type: ignore[attr-defined]
 ):
 self.cfgfile_parser.add_section(group_name)
 # add provider's specific options
 for opt, optdict in options:
 if not isinstance(optdict.get("action", "store"), str):
 optdict["action"] = "callback"
 with warnings.catch_warnings():
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 self.add_optik_option(provider, group, opt, optdict)
 
 def add_optik_option(
 self,
 provider: ConfigProvider,
 optikcontainer: optparse.OptionParser | optparse.OptionGroup,
 opt: str,
 optdict: OptionDict,
 ) -> None:  # pragma: no cover
 """DEPRECATED."""
 warnings.warn(
 "add_optik_option has been deprecated. Options should be automatically "
 "added by initializing an ArgumentsProvider.",
 DeprecationWarning,
 stacklevel=2,
 )
 with warnings.catch_warnings():
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 args, optdict = self.optik_option(provider, opt, optdict)
 option = optikcontainer.add_option(*args, **optdict)
 self._all_options[opt] = provider
 self._maxlevel = max(self._maxlevel, option.level or 0)
 
 def optik_option(
 self, provider: ConfigProvider, opt: str, optdict: OptionDict
 ) -> tuple[list[str], OptionDict]:  # pragma: no cover
 """DEPRECATED: Get our personal option definition and return a suitable form for
 use with optik/optparse.
 """
 warnings.warn(
 "optik_option has been deprecated. Parsing of option dictionaries should be done "
 "automatically by initializing an ArgumentsProvider.",
 DeprecationWarning,
 stacklevel=2,
 )
 optdict = copy.copy(optdict)
 if "action" in optdict:
 self._nocallback_options[provider] = opt
 else:
 optdict["action"] = "callback"
 optdict["callback"] = self.cb_set_provider_option
 # default is handled here and *must not* be given to optik if you
 # want the whole machinery to work
 if "default" in optdict:
 if (
 "help" in optdict
 and optdict.get("default") is not None
 and optdict["action"] not in ("store_true", "store_false")
 ):
 optdict["help"] += " [current: %default]"  # type: ignore[operator]
 del optdict["default"]
 args = ["--" + str(opt)]
 if "short" in optdict:
 self._short_options[optdict["short"]] = opt  # type: ignore[index]
 args.append("-" + optdict["short"])  # type: ignore[operator]
 del optdict["short"]
 # cleanup option definition dict before giving it to optik
 for key in list(optdict.keys()):
 if key not in self._optik_option_attrs:
 optdict.pop(key)
 return args, optdict
 
 def generate_config(
 self, stream: TextIO | None = None, skipsections: tuple[str, ...] = ()
 ) -> None:  # pragma: no cover
 """DEPRECATED: Write a configuration file according to the current configuration
 into the given stream or stdout.
 """
 warnings.warn(
 "generate_config has been deprecated. It will be removed in pylint 3.0.",
 DeprecationWarning,
 stacklevel=2,
 )
 options_by_section = {}
 sections = []
 for group in sorted(
 self._arg_parser._action_groups,
 key=lambda x: (x.title != "Main", x.title),
 ):
 group_name = group.title
 assert group_name
 if group_name in skipsections:
 continue
 
 options = []
 option_actions = [
 i
 for i in group._group_actions
 if not isinstance(i, argparse._SubParsersAction)
 ]
 for opt in sorted(option_actions, key=lambda x: x.option_strings[0][2:]):
 if "--help" in opt.option_strings:
 continue
 
 optname = opt.option_strings[0][2:]
 
 try:
 optdict = self._option_dicts[optname]
 except KeyError:
 continue
 
 options.append(
 (
 optname,
 optdict,
 getattr(self.config, optname.replace("-", "_")),
 )
 )
 
 options = [
 (n, d, v) for (n, d, v) in options if not d.get("deprecated")
 ]
 
 if options:
 sections.append(group_name)
 options_by_section[group_name] = options
 stream = stream or sys.stdout
 printed = False
 for section in sections:
 if printed:
 print("\n", file=stream)
 with warnings.catch_warnings():
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 utils.format_section(
 stream, section.upper(), sorted(options_by_section[section])
 )
 printed = True
 
 def load_provider_defaults(self) -> None:  # pragma: no cover
 """DEPRECATED: Initialize configuration using default values."""
 warnings.warn(
 "load_provider_defaults has been deprecated. Parsing of option defaults should be done "
 "automatically by initializing an ArgumentsProvider.",
 DeprecationWarning,
 stacklevel=2,
 )
 for provider in self.options_providers:
 with warnings.catch_warnings():
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 provider.load_defaults()
 
 def read_config_file(
 self, config_file: Path | None = None, verbose: bool = False
 ) -> None:  # pragma: no cover
 """DEPRECATED: Read the configuration file but do not load it (i.e. dispatching
 values to each option's provider).
 
 :raises OSError: When the specified config file doesn't exist
 """
 warnings.warn(
 "read_config_file has been deprecated. It will be removed in pylint 3.0.",
 DeprecationWarning,
 stacklevel=2,
 )
 if not config_file:
 if verbose:
 print(
 "No config file found, using default configuration", file=sys.stderr
 )
 return
 config_file = Path(os.path.expandvars(config_file)).expanduser()
 if not config_file.exists():
 raise OSError(f"The config file {str(config_file)} doesn't exist!")
 parser = self.cfgfile_parser
 if config_file.suffix == ".toml":
 try:
 self._parse_toml(config_file, parser)
 except tomllib.TOMLDecodeError:
 pass
 else:
 # Use this encoding in order to strip the BOM marker, if any.
 with open(config_file, encoding="utf_8_sig") as fp:
 parser.read_file(fp)
 # normalize each section's title
 for sect, values in list(parser._sections.items()):  # type: ignore[attr-defined]
 if sect.startswith("pylint."):
 sect = sect[len("pylint.") :]
 if not sect.isupper() and values:
 parser._sections[sect.upper()] = values  # type: ignore[attr-defined]
 
 if verbose:
 print(f"Using config file '{config_file}'", file=sys.stderr)
 
 @staticmethod
 def _parse_toml(
 config_file: Path, parser: configparser.ConfigParser
 ) -> None:  # pragma: no cover
 """DEPRECATED: Parse and handle errors of a toml configuration file.
 
 TODO: 3.0: Remove deprecated method.
 """
 with open(config_file, mode="rb") as fp:
 content = tomllib.load(fp)
 try:
 sections_values = content["tool"]["pylint"]
 except KeyError:
 return
 for section, values in sections_values.items():
 section_name = section.upper()
 # TOML has rich types, convert values to
 # strings as ConfigParser expects.
 if not isinstance(values, dict):
 continue
 for option, value in values.items():
 if isinstance(value, bool):
 values[option] = "yes" if value else "no"
 elif isinstance(value, list):
 values[option] = ",".join(value)
 else:
 values[option] = str(value)
 for option, value in values.items():
 try:
 parser.set(section_name, option, value=value)
 except configparser.NoSectionError:
 parser.add_section(section_name)
 parser.set(section_name, option, value=value)
 
 def load_config_file(self) -> None:  # pragma: no cover
 """DEPRECATED: Dispatch values previously read from a configuration file to each
 option's provider.
 """
 warnings.warn(
 "load_config_file has been deprecated. It will be removed in pylint 3.0.",
 DeprecationWarning,
 stacklevel=2,
 )
 parser = self.cfgfile_parser
 for section in parser.sections():
 for option, value in parser.items(section):
 try:
 self.global_set_option(option, value)
 except (KeyError, optparse.OptionError):
 continue
 
 def load_configuration(self, **kwargs: Any) -> None:  # pragma: no cover
 """DEPRECATED: Override configuration according to given parameters."""
 warnings.warn(
 "load_configuration has been deprecated. It will be removed in pylint 3.0.",
 DeprecationWarning,
 stacklevel=2,
 )
 with warnings.catch_warnings():
 warnings.filterwarnings("ignore", category=DeprecationWarning)
 return self.load_configuration_from_config(kwargs)
 
 def load_configuration_from_config(
 self, config: dict[str, Any]
 ) -> None:  # pragma: no cover
 warnings.warn(
 "DEPRECATED: load_configuration_from_config has been deprecated. It will be removed in pylint 3.0.",
 DeprecationWarning,
 stacklevel=2,
 )
 for opt, opt_value in config.items():
 opt = opt.replace("_", "-")
 provider = self._all_options[opt]
 provider.set_option(opt, opt_value)
 
 def load_command_line_configuration(
 self, args: list[str] | None = None
 ) -> list[str]:  # pragma: no cover
 """DEPRECATED: Override configuration according to command line parameters.
 
 return additional arguments
 """
 warnings.warn(
 "load_command_line_configuration has been deprecated. It will be removed in pylint 3.0.",
 DeprecationWarning,
 stacklevel=2,
 )
 args = sys.argv[1:] if args is None else list(args)
 (options, args) = self.cmdline_parser.parse_args(args=args)
 for provider in self._nocallback_options:
 config = provider.config
 for attr in config.__dict__.keys():
 value = getattr(options, attr, None)
 if value is None:
 continue
 setattr(config, attr, value)
 return args  # type: ignore[return-value]
 
 def help(self, level: int | None = None) -> str:
 """Return the usage string based on the available options."""
 if level is not None:
 warnings.warn(
 "Supplying a 'level' argument to help() has been deprecated."
 "You can call help() without any arguments.",
 DeprecationWarning,
 stacklevel=2,
 )
 return self._arg_parser.format_help()
 
 def cb_set_provider_option(  # pragma: no cover
 self, option: Any, opt: Any, value: Any, parser: Any
 ) -> None:
 """DEPRECATED: Optik callback for option setting."""
 # TODO: 3.0: Remove deprecated method.
 warnings.warn(
 "cb_set_provider_option has been deprecated. It will be removed in pylint 3.0.",
 DeprecationWarning,
 stacklevel=2,
 )
 if opt.startswith("--"):
 # remove -- on long option
 opt = opt[2:]
 else:
 # short option, get its long equivalent
 opt = self._short_options[opt[1:]]
 # trick since we can't set action='store_true' on options
 if value is None:
 value = 1
 self.set_option(opt, value)
 
 def global_set_option(self, opt: str, value: Any) -> None:  # pragma: no cover
 """DEPRECATED: Set option on the correct option provider."""
 # TODO: 3.0: Remove deprecated method.
 warnings.warn(
 "global_set_option has been deprecated. You can use _arguments_manager.set_option "
 "or linter.set_option to set options on the global configuration object.",
 DeprecationWarning,
 stacklevel=2,
 )
 self.set_option(opt, value)
 
 def _generate_config_file(self, *, minimal: bool = False) -> str:
 """Write a configuration file according to the current configuration into
 stdout.
 """
 toml_doc = tomlkit.document()
 tool_table = tomlkit.table(is_super_table=True)
 toml_doc.add(tomlkit.key("tool"), tool_table)
 
 pylint_tool_table = tomlkit.table(is_super_table=True)
 tool_table.add(tomlkit.key("pylint"), pylint_tool_table)
 
 for group in sorted(
 self._arg_parser._action_groups,
 key=lambda x: (x.title != "Main", x.title),
 ):
 # Skip the options section with the --help option
 if group.title in {"options", "optional arguments", "Commands"}:
 continue
 
 # Skip sections without options such as "positional arguments"
 if not group._group_actions:
 continue
 
 group_table = tomlkit.table()
 option_actions = [
 i
 for i in group._group_actions
 if not isinstance(i, argparse._SubParsersAction)
 ]
 for action in sorted(option_actions, key=lambda x: x.option_strings[0][2:]):
 optname = action.option_strings[0][2:]
 
 # We skip old name options that don't have their own optdict
 try:
 optdict = self._option_dicts[optname]
 except KeyError:
 continue
 
 if optdict.get("hide_from_config_file"):
 continue
 
 # Add help comment
 if not minimal:
 help_msg = optdict.get("help", "")
 assert isinstance(help_msg, str)
 help_text = textwrap.wrap(help_msg, width=79)
 for line in help_text:
 group_table.add(tomlkit.comment(line))
 
 # Get current value of option
 value = getattr(self.config, optname.replace("-", "_"))
 
 # Create a comment if the option has no value
 if not value:
 if not minimal:
 group_table.add(tomlkit.comment(f"{optname} ="))
 group_table.add(tomlkit.nl())
 continue
 
 # Skip deprecated options
 if "kwargs" in optdict:
 assert isinstance(optdict["kwargs"], dict)
 if "new_names" in optdict["kwargs"]:
 continue
 
 # Tomlkit doesn't support regular expressions
 if isinstance(value, re.Pattern):
 value = value.pattern
 elif isinstance(value, (list, tuple)) and isinstance(
 value[0], re.Pattern
 ):
 value = [i.pattern for i in value]
 
 # Handle tuples that should be strings
 if optdict.get("type") == "py_version":
 value = ".".join(str(i) for i in value)
 
 # Check if it is default value if we are in minimal mode
 if minimal and value == optdict.get("default"):
 continue
 
 # Add to table
 group_table.add(optname, value)
 group_table.add(tomlkit.nl())
 
 assert group.title
 if group_table:
 pylint_tool_table.add(group.title.lower(), group_table)
 
 toml_string = tomlkit.dumps(toml_doc)
 
 # Make sure the string we produce is valid toml and can be parsed
 tomllib.loads(toml_string)
 
 return str(toml_string)
 
 def set_option(
 self,
 optname: str,
 value: Any,
 action: str | None = "default_value",
 optdict: None | str | OptionDict = "default_value",
 ) -> None:
 """Set an option on the namespace object."""
 # TODO: 3.0: Remove deprecated arguments.
 if action != "default_value":
 warnings.warn(
 "The 'action' argument has been deprecated. You can use set_option "
 "without the 'action' or 'optdict' arguments.",
 DeprecationWarning,
 stacklevel=2,
 )
 if optdict != "default_value":
 warnings.warn(
 "The 'optdict' argument has been deprecated. You can use set_option "
 "without the 'action' or 'optdict' arguments.",
 DeprecationWarning,
 stacklevel=2,
 )
 
 self.config = self._arg_parser.parse_known_args(
 [f"--{optname.replace('_', '-')}", _parse_rich_type_value(value)],
 self.config,
 )[0]
 
 |