Coverage for C:\Repos\ekr-pylint\pylint\config\config_file_parser.py: 24%

75 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"""Configuration file parser class.""" 

6 

7from __future__ import annotations 

8 

9import configparser 

10import os 

11import sys 

12import warnings 

13from pathlib import Path 

14from typing import TYPE_CHECKING 

15 

16from pylint.config.utils import _parse_rich_type_value 

17 

18if sys.version_info >= (3, 11): 

19 import tomllib 

20else: 

21 import tomli as tomllib 

22 

23if TYPE_CHECKING: 

24 from pylint.lint import PyLinter 

25 

26 

27class _ConfigurationFileParser: 

28 """Class to parse various formats of configuration files.""" 

29 

30 def __init__(self, verbose: bool, linter: PyLinter) -> None: 

31 self.verbose_mode = verbose 

32 self.linter = linter 

33 

34 @staticmethod 

35 def _parse_ini_file(file_path: Path) -> tuple[dict[str, str], list[str]]: 

36 """Parse and handle errors of a ini configuration file.""" 

37 parser = configparser.ConfigParser(inline_comment_prefixes=("#", ";")) 

38 

39 # Use this encoding in order to strip the BOM marker, if any. 

40 with open(file_path, encoding="utf_8_sig") as fp: 

41 parser.read_file(fp) 

42 

43 config_content: dict[str, str] = {} 

44 options: list[str] = [] 

45 for section in parser.sections(): 

46 if "setup.cfg" in str(file_path) and not section.startswith("pylint"): 

47 if section.lower() == "master": 

48 # TODO: 3.0: Remove deprecated handling of master, only allow 'pylint.' sections 

49 warnings.warn( 

50 "The use of 'MASTER' or 'master' as configuration section for pylint " 

51 "has been deprecated, as it's bad practice to not start sections titles with the " 

52 "tool name. Please use 'pylint.main' instead.", 

53 UserWarning, 

54 ) 

55 else: 

56 continue 

57 for opt, value in parser[section].items(): 

58 value = value.replace("\n", "") 

59 config_content[opt] = value 

60 options += [f"--{opt}", value] 

61 return config_content, options 

62 

63 def _parse_toml_file(self, file_path: Path) -> tuple[dict[str, str], list[str]]: 

64 """Parse and handle errors of a toml configuration file.""" 

65 try: 

66 with open(file_path, mode="rb") as fp: 

67 content = tomllib.load(fp) 

68 except tomllib.TOMLDecodeError as e: 

69 self.linter.add_message("config-parse-error", line=0, args=str(e)) 

70 return {}, [] 

71 

72 try: 

73 sections_values = content["tool"]["pylint"] 

74 except KeyError: 

75 return {}, [] 

76 

77 config_content: dict[str, str] = {} 

78 options: list[str] = [] 

79 for opt, values in sections_values.items(): 

80 if isinstance(values, dict): 

81 for config, value in values.items(): 

82 value = _parse_rich_type_value(value) 

83 config_content[config] = value 

84 options += [f"--{config}", value] 

85 else: 

86 values = _parse_rich_type_value(values) 

87 config_content[opt] = values 

88 options += [f"--{opt}", values] 

89 return config_content, options 

90 

91 def parse_config_file( 

92 self, file_path: Path | None 

93 ) -> tuple[dict[str, str], list[str]]: 

94 """Parse a config file and return str-str pairs.""" 

95 if file_path is None: 

96 if self.verbose_mode: 

97 print( 

98 "No config file found, using default configuration", file=sys.stderr 

99 ) 

100 return {}, [] 

101 

102 file_path = Path(os.path.expandvars(file_path)).expanduser() 

103 if not file_path.exists(): 

104 raise OSError(f"The config file {file_path} doesn't exist!") 

105 

106 if self.verbose_mode: 

107 print(f"Using config file {file_path}", file=sys.stderr) 

108 

109 try: 

110 if file_path.suffix == ".toml": 

111 return self._parse_toml_file(file_path) 

112 return self._parse_ini_file(file_path) 

113 except (configparser.Error, tomllib.TOMLDecodeError) as e: 

114 self.linter.add_message("config-parse-error", line=0, args=str(e)) 

115 return {}, []