Coverage for C:\Repos\ekr-pylint\pylint\config\utils.py: 22%

98 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-05-24 10:21 -0500

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# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt 

4 

5"""Utils for arguments/options parsing and handling.""" 

6 

7from __future__ import annotations 

8 

9import re 

10import warnings 

11from collections.abc import Callable, Sequence 

12from pathlib import Path 

13from typing import TYPE_CHECKING, Any 

14 

15from pylint import extensions, utils 

16from pylint.config.argument import ( 

17 _CallableArgument, 

18 _ExtendArgument, 

19 _StoreArgument, 

20 _StoreNewNamesArgument, 

21 _StoreOldNamesArgument, 

22 _StoreTrueArgument, 

23) 

24from pylint.config.callback_actions import _CallbackAction 

25from pylint.config.exceptions import ArgumentPreprocessingError 

26 

27if TYPE_CHECKING: 

28 from pylint.lint.run import Run 

29 

30 

31def _convert_option_to_argument( 

32 opt: str, optdict: dict[str, Any] 

33) -> ( 

34 _StoreArgument 

35 | _StoreTrueArgument 

36 | _CallableArgument 

37 | _StoreOldNamesArgument 

38 | _StoreNewNamesArgument 

39 | _ExtendArgument 

40): 

41 """Convert an optdict to an Argument class instance.""" 

42 if "level" in optdict and "hide" not in optdict: 

43 warnings.warn( 

44 "The 'level' key in optdicts has been deprecated. " 

45 "Use 'hide' with a boolean to hide an option from the help message.", 

46 DeprecationWarning, 

47 ) 

48 

49 # Get the long and short flags 

50 flags = [f"--{opt}"] 

51 if "short" in optdict: 

52 flags += [f"-{optdict['short']}"] 

53 

54 # Get the action type 

55 action = optdict.get("action", "store") 

56 

57 if action == "store_true": 

58 return _StoreTrueArgument( 

59 flags=flags, 

60 action=action, 

61 default=optdict.get("default", True), 

62 arg_help=optdict.get("help", ""), 

63 hide_help=optdict.get("hide", False), 

64 section=optdict.get("group", None), 

65 ) 

66 if not isinstance(action, str) and issubclass(action, _CallbackAction): 

67 return _CallableArgument( 

68 flags=flags, 

69 action=action, 

70 arg_help=optdict.get("help", ""), 

71 kwargs=optdict.get("kwargs", {}), 

72 hide_help=optdict.get("hide", False), 

73 section=optdict.get("group", None), 

74 metavar=optdict.get("metavar", None), 

75 ) 

76 try: 

77 default = optdict["default"] 

78 except KeyError: 

79 warnings.warn( 

80 "An option dictionary should have a 'default' key to specify " 

81 "the option's default value. This key will be required in pylint " 

82 "3.0. It is not required for 'store_true' and callable actions.", 

83 DeprecationWarning, 

84 ) 

85 default = None 

86 if action == "extend": 

87 return _ExtendArgument( 

88 flags=flags, 

89 action=action, 

90 default=[] if default is None else default, 

91 arg_type=optdict["type"], 

92 choices=optdict.get("choices", None), 

93 arg_help=optdict.get("help", ""), 

94 metavar=optdict.get("metavar", ""), 

95 hide_help=optdict.get("hide", False), 

96 section=optdict.get("group", None), 

97 dest=optdict.get("dest", None), 

98 ) 

99 if "kwargs" in optdict: 

100 if "old_names" in optdict["kwargs"]: 

101 return _StoreOldNamesArgument( 

102 flags=flags, 

103 default=default, 

104 arg_type=optdict["type"], 

105 choices=optdict.get("choices", None), 

106 arg_help=optdict.get("help", ""), 

107 metavar=optdict.get("metavar", ""), 

108 hide_help=optdict.get("hide", False), 

109 kwargs=optdict.get("kwargs", {}), 

110 section=optdict.get("group", None), 

111 ) 

112 if "new_names" in optdict["kwargs"]: 

113 return _StoreNewNamesArgument( 

114 flags=flags, 

115 default=default, 

116 arg_type=optdict["type"], 

117 choices=optdict.get("choices", None), 

118 arg_help=optdict.get("help", ""), 

119 metavar=optdict.get("metavar", ""), 

120 hide_help=optdict.get("hide", False), 

121 kwargs=optdict.get("kwargs", {}), 

122 section=optdict.get("group", None), 

123 ) 

124 if "dest" in optdict: 

125 return _StoreOldNamesArgument( 

126 flags=flags, 

127 default=default, 

128 arg_type=optdict["type"], 

129 choices=optdict.get("choices", None), 

130 arg_help=optdict.get("help", ""), 

131 metavar=optdict.get("metavar", ""), 

132 hide_help=optdict.get("hide", False), 

133 kwargs={"old_names": [optdict["dest"]]}, 

134 section=optdict.get("group", None), 

135 ) 

136 return _StoreArgument( 

137 flags=flags, 

138 action=action, 

139 default=default, 

140 arg_type=optdict["type"], 

141 choices=optdict.get("choices", None), 

142 arg_help=optdict.get("help", ""), 

143 metavar=optdict.get("metavar", ""), 

144 hide_help=optdict.get("hide", False), 

145 section=optdict.get("group", None), 

146 ) 

147 

148 

149def _parse_rich_type_value(value: Any) -> str: 

150 """Parse rich (toml) types into strings.""" 

151 if isinstance(value, (list, tuple)): 

152 return ",".join(_parse_rich_type_value(i) for i in value) 

153 if isinstance(value, re.Pattern): 

154 return value.pattern 

155 if isinstance(value, dict): 

156 return ",".join(f"{k}:{v}" for k, v in value.items()) 

157 return str(value) 

158 

159 

160# pylint: disable-next=unused-argument 

161def _init_hook(run: Run, value: str | None) -> None: 

162 """Execute arbitrary code from the init_hook. 

163 

164 This can be used to set the 'sys.path' for example. 

165 """ 

166 assert value is not None 

167 exec(value) # pylint: disable=exec-used 

168 

169 

170def _set_rcfile(run: Run, value: str | None) -> None: 

171 """Set the rcfile.""" 

172 assert value is not None 

173 run._rcfile = value 

174 

175 

176def _set_output(run: Run, value: str | None) -> None: 

177 """Set the output.""" 

178 assert value is not None 

179 run._output = value 

180 

181 

182def _add_plugins(run: Run, value: str | None) -> None: 

183 """Add plugins to the list of loadable plugins.""" 

184 assert value is not None 

185 run._plugins.extend(utils._splitstrip(value)) 

186 

187 

188def _set_verbose_mode(run: Run, value: str | None) -> None: 

189 assert value is None 

190 run.verbose = True 

191 

192 

193def _enable_all_extensions(run: Run, value: str | None) -> None: 

194 """Enable all extensions.""" 

195 assert value is None 

196 for filename in Path(extensions.__file__).parent.iterdir(): 

197 if filename.suffix == ".py" and not filename.stem.startswith("_"): 

198 extension_name = f"pylint.extensions.{filename.stem}" 

199 if extension_name not in run._plugins: 

200 run._plugins.append(extension_name) 

201 

202 

203PREPROCESSABLE_OPTIONS: dict[ 

204 str, tuple[bool, Callable[[Run, str | None], None]] 

205] = { # pylint: disable=consider-using-namedtuple-or-dataclass 

206 "--init-hook": (True, _init_hook), 

207 "--rcfile": (True, _set_rcfile), 

208 "--output": (True, _set_output), 

209 "--load-plugins": (True, _add_plugins), 

210 "--verbose": (False, _set_verbose_mode), 

211 "-v": (False, _set_verbose_mode), 

212 "--enable-all-extensions": (False, _enable_all_extensions), 

213} 

214 

215 

216def _preprocess_options(run: Run, args: Sequence[str]) -> list[str]: 

217 """Pre-process options before full config parsing has started.""" 

218 processed_args: list[str] = [] 

219 

220 i = 0 

221 while i < len(args): 

222 argument = args[i] 

223 if not argument.startswith("-"): 

224 processed_args.append(argument) 

225 i += 1 

226 continue 

227 

228 try: 

229 option, value = argument.split("=", 1) 

230 except ValueError: 

231 option, value = argument, None 

232 

233 if option not in PREPROCESSABLE_OPTIONS: 

234 processed_args.append(argument) 

235 i += 1 

236 continue 

237 

238 takearg, cb = PREPROCESSABLE_OPTIONS[option] 

239 

240 if takearg and value is None: 

241 i += 1 

242 if i >= len(args) or args[i].startswith("-"): 

243 raise ArgumentPreprocessingError(f"Option {option} expects a value") 

244 value = args[i] 

245 elif not takearg and value is not None: 

246 raise ArgumentPreprocessingError(f"Option {option} doesn't expects a value") 

247 

248 cb(run, value) 

249 i += 1 

250 

251 return processed_args