Coverage for C:\Repos\ekr-pylint\pylint\config\callback_actions.py: 46%

120 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# pylint: disable=too-many-arguments, redefined-builtin, duplicate-code 

6 

7"""Callback actions for various options.""" 

8 

9from __future__ import annotations 

10 

11import abc 

12import argparse 

13import sys 

14import warnings 

15from collections.abc import Sequence 

16from pathlib import Path 

17from typing import TYPE_CHECKING, Any 

18 

19from pylint import exceptions, extensions, interfaces, utils 

20 

21if TYPE_CHECKING: 

22 from pylint.config.help_formatter import _HelpFormatter 

23 from pylint.lint import PyLinter 

24 from pylint.lint.run import Run 

25 

26 

27class _CallbackAction(argparse.Action): 

28 """Custom callback action.""" 

29 

30 @abc.abstractmethod 

31 def __call__( 

32 self, 

33 parser: argparse.ArgumentParser, 

34 namespace: argparse.Namespace, 

35 values: str | Sequence[Any] | None, 

36 option_string: str | None = None, 

37 ) -> None: 

38 raise NotImplementedError # pragma: no cover 

39 

40 

41class _DoNothingAction(_CallbackAction): 

42 """Action that just passes. 

43 

44 This action is used to allow pre-processing of certain options 

45 without erroring when they are then processed again by argparse. 

46 """ 

47 

48 def __call__( 

49 self, 

50 parser: argparse.ArgumentParser, 

51 namespace: argparse.Namespace, 

52 values: str | Sequence[Any] | None, 

53 option_string: str | None = None, 

54 ) -> None: 

55 return None 

56 

57 

58class _ExtendAction(argparse._AppendAction): 

59 """Action that adds the value to a pre-existing list. 

60 

61 It is directly copied from the stdlib implementation which is only available 

62 on 3.8+. 

63 """ 

64 

65 def __call__( 

66 self, 

67 parser: argparse.ArgumentParser, 

68 namespace: argparse.Namespace, 

69 values: str | Sequence[Any] | None, 

70 option_string: str | None = None, 

71 ) -> None: 

72 assert isinstance(values, (tuple, list)) 

73 current = getattr(namespace, self.dest, []) 

74 assert isinstance(current, list) 

75 current.extend(values) 

76 setattr(namespace, self.dest, current) 

77 

78 

79class _AccessRunObjectAction(_CallbackAction): 

80 """Action that has access to the Run object.""" 

81 

82 def __init__( 

83 self, 

84 option_strings: Sequence[str], 

85 dest: str, 

86 nargs: None = None, 

87 const: None = None, 

88 default: None = None, 

89 type: None = None, 

90 choices: None = None, 

91 required: bool = False, 

92 help: str = "", 

93 metavar: str = "", 

94 **kwargs: Run, 

95 ) -> None: 

96 self.run = kwargs["Run"] 

97 

98 super().__init__( 

99 option_strings, 

100 dest, 

101 0, 

102 const, 

103 default, 

104 type, 

105 choices, 

106 required, 

107 help, 

108 metavar, 

109 ) 

110 

111 @abc.abstractmethod 

112 def __call__( 

113 self, 

114 parser: argparse.ArgumentParser, 

115 namespace: argparse.Namespace, 

116 values: str | Sequence[Any] | None, 

117 option_string: str | None = None, 

118 ) -> None: 

119 raise NotImplementedError # pragma: no cover 

120 

121 

122class _MessageHelpAction(_CallbackAction): 

123 """Display the help message of a message.""" 

124 

125 def __init__( 

126 self, 

127 option_strings: Sequence[str], 

128 dest: str, 

129 nargs: None = None, 

130 const: None = None, 

131 default: None = None, 

132 type: None = None, 

133 choices: None = None, 

134 required: bool = False, 

135 help: str = "", 

136 metavar: str = "", 

137 **kwargs: Run, 

138 ) -> None: 

139 self.run = kwargs["Run"] 

140 super().__init__( 

141 option_strings, 

142 dest, 

143 "+", 

144 const, 

145 default, 

146 type, 

147 choices, 

148 required, 

149 help, 

150 metavar, 

151 ) 

152 

153 def __call__( 

154 self, 

155 parser: argparse.ArgumentParser, 

156 namespace: argparse.Namespace, 

157 values: str | Sequence[str] | None, 

158 option_string: str | None = "--help-msg", 

159 ) -> None: 

160 assert isinstance(values, (list, tuple)) 

161 self.run.linter.msgs_store.help_message(values) 

162 sys.exit(0) 

163 

164 

165class _ListMessagesAction(_AccessRunObjectAction): 

166 """Display all available messages.""" 

167 

168 def __call__( 

169 self, 

170 parser: argparse.ArgumentParser, 

171 namespace: argparse.Namespace, 

172 values: str | Sequence[Any] | None, 

173 option_string: str | None = "--list-enabled", 

174 ) -> None: 

175 self.run.linter.msgs_store.list_messages() 

176 sys.exit(0) 

177 

178 

179class _ListMessagesEnabledAction(_AccessRunObjectAction): 

180 """Display all enabled messages.""" 

181 

182 def __call__( 

183 self, 

184 parser: argparse.ArgumentParser, 

185 namespace: argparse.Namespace, 

186 values: str | Sequence[Any] | None, 

187 option_string: str | None = "--list-msgs-enabled", 

188 ) -> None: 

189 self.run.linter.list_messages_enabled() 

190 sys.exit(0) 

191 

192 

193class _ListCheckGroupsAction(_AccessRunObjectAction): 

194 """Display all the check groups that pylint knows about.""" 

195 

196 def __call__( 

197 self, 

198 parser: argparse.ArgumentParser, 

199 namespace: argparse.Namespace, 

200 values: str | Sequence[Any] | None, 

201 option_string: str | None = "--list-groups", 

202 ) -> None: 

203 for check in self.run.linter.get_checker_names(): 

204 print(check) 

205 sys.exit(0) 

206 

207 

208class _ListConfidenceLevelsAction(_AccessRunObjectAction): 

209 """Display all the confidence levels that pylint knows about.""" 

210 

211 def __call__( 

212 self, 

213 parser: argparse.ArgumentParser, 

214 namespace: argparse.Namespace, 

215 values: str | Sequence[Any] | None, 

216 option_string: str | None = "--list-conf-levels", 

217 ) -> None: 

218 for level in interfaces.CONFIDENCE_LEVELS: 

219 print(f"%-18s: {level}") 

220 sys.exit(0) 

221 

222 

223class _ListExtensionsAction(_AccessRunObjectAction): 

224 """Display all extensions under pylint.extensions.""" 

225 

226 def __call__( 

227 self, 

228 parser: argparse.ArgumentParser, 

229 namespace: argparse.Namespace, 

230 values: str | Sequence[Any] | None, 

231 option_string: str | None = "--list-extensions", 

232 ) -> None: 

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

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

235 extension_name, _, _ = filename.stem.partition(".") 

236 print(f"pylint.extensions.{extension_name}") 

237 sys.exit(0) 

238 

239 

240class _FullDocumentationAction(_AccessRunObjectAction): 

241 """Display the full documentation.""" 

242 

243 def __call__( 

244 self, 

245 parser: argparse.ArgumentParser, 

246 namespace: argparse.Namespace, 

247 values: str | Sequence[Any] | None, 

248 option_string: str | None = "--full-documentation", 

249 ) -> None: 

250 utils.print_full_documentation(self.run.linter) 

251 sys.exit(0) 

252 

253 

254class _GenerateRCFileAction(_AccessRunObjectAction): 

255 """Generate a pylintrc file.""" 

256 

257 def __call__( 

258 self, 

259 parser: argparse.ArgumentParser, 

260 namespace: argparse.Namespace, 

261 values: str | Sequence[Any] | None, 

262 option_string: str | None = "--generate-rcfile", 

263 ) -> None: 

264 # TODO: 2.15: Deprecate this after discussion about this removal has been completed. 

265 with warnings.catch_warnings(): 

266 warnings.filterwarnings("ignore", category=DeprecationWarning) 

267 self.run.linter.generate_config(skipsections=("Commands",)) 

268 sys.exit(0) 

269 

270 

271class _GenerateConfigFileAction(_AccessRunObjectAction): 

272 """Generate a .toml format configuration file.""" 

273 

274 def __call__( 

275 self, 

276 parser: argparse.ArgumentParser, 

277 namespace: argparse.Namespace, 

278 values: str | Sequence[Any] | None, 

279 option_string: str | None = "--generate-toml-config", 

280 ) -> None: 

281 self.run.linter._generate_config_file() 

282 sys.exit(0) 

283 

284 

285class _ErrorsOnlyModeAction(_AccessRunObjectAction): 

286 """Turn on errors-only mode. 

287 

288 Error mode: 

289 * disable all but error messages 

290 * disable the 'miscellaneous' checker which can be safely deactivated in 

291 debug 

292 * disable reports 

293 * do not save execution information 

294 """ 

295 

296 def __call__( 

297 self, 

298 parser: argparse.ArgumentParser, 

299 namespace: argparse.Namespace, 

300 values: str | Sequence[Any] | None, 

301 option_string: str | None = "--errors-only", 

302 ) -> None: 

303 self.run.linter._error_mode = True 

304 

305 

306class _LongHelpAction(_AccessRunObjectAction): 

307 """Display the long help message.""" 

308 

309 def __call__( 

310 self, 

311 parser: argparse.ArgumentParser, 

312 namespace: argparse.Namespace, 

313 values: str | Sequence[Any] | None, 

314 option_string: str | None = "--long-help", 

315 ) -> None: 

316 formatter: _HelpFormatter = self.run.linter._arg_parser._get_formatter() # type: ignore[assignment] 

317 

318 # Add extra info as epilog to the help message 

319 self.run.linter._arg_parser.epilog = formatter.get_long_description() 

320 print(self.run.linter.help()) 

321 

322 sys.exit(0) 

323 

324 

325class _AccessLinterObjectAction(_CallbackAction): 

326 """Action that has access to the Linter object.""" 

327 

328 def __init__( 

329 self, 

330 option_strings: Sequence[str], 

331 dest: str, 

332 nargs: None = None, 

333 const: None = None, 

334 default: None = None, 

335 type: None = None, 

336 choices: None = None, 

337 required: bool = False, 

338 help: str = "", 

339 metavar: str = "", 

340 **kwargs: PyLinter, 

341 ) -> None: 

342 self.linter = kwargs["linter"] 

343 

344 super().__init__( 

345 option_strings, 

346 dest, 

347 1, 

348 const, 

349 default, 

350 type, 

351 choices, 

352 required, 

353 help, 

354 metavar, 

355 ) 

356 

357 @abc.abstractmethod 

358 def __call__( 

359 self, 

360 parser: argparse.ArgumentParser, 

361 namespace: argparse.Namespace, 

362 values: str | Sequence[Any] | None, 

363 option_string: str | None = None, 

364 ) -> None: 

365 raise NotImplementedError # pragma: no cover 

366 

367 

368class _DisableAction(_AccessLinterObjectAction): 

369 """Callback action for disabling a message.""" 

370 

371 def __call__( 

372 self, 

373 parser: argparse.ArgumentParser, 

374 namespace: argparse.Namespace, 

375 values: str | Sequence[Any] | None, 

376 option_string: str | None = "--disable", 

377 ) -> None: 

378 assert isinstance(values, (tuple, list)) 

379 msgids = utils._check_csv(values[0]) 

380 for msgid in msgids: 

381 try: 

382 self.linter.disable(msgid) 

383 except exceptions.UnknownMessageError: 

384 msg = f"{option_string}. Don't recognize message {msgid}." 

385 self.linter.add_message("bad-option-value", args=msg, line=0) 

386 

387 

388class _EnableAction(_AccessLinterObjectAction): 

389 """Callback action for enabling a message.""" 

390 

391 def __call__( 

392 self, 

393 parser: argparse.ArgumentParser, 

394 namespace: argparse.Namespace, 

395 values: str | Sequence[Any] | None, 

396 option_string: str | None = "--enable", 

397 ) -> None: 

398 assert isinstance(values, (tuple, list)) 

399 msgids = utils._check_csv(values[0]) 

400 for msgid in msgids: 

401 try: 

402 self.linter.enable(msgid) 

403 except exceptions.UnknownMessageError: 

404 msg = f"{option_string}. Don't recognize message {msgid}." 

405 self.linter.add_message("bad-option-value", args=msg, line=0) 

406 

407 

408class _OutputFormatAction(_AccessLinterObjectAction): 

409 """Callback action for setting the output format.""" 

410 

411 def __call__( 

412 self, 

413 parser: argparse.ArgumentParser, 

414 namespace: argparse.Namespace, 

415 values: str | Sequence[Any] | None, 

416 option_string: str | None = "--enable", 

417 ) -> None: 

418 assert isinstance(values, (tuple, list)) 

419 assert isinstance( 

420 values[0], str 

421 ), "'output-format' should be a comma separated string of reporters" 

422 self.linter._load_reporters(values[0])