Coverage for C:\Repos\ekr-pylint\pylint\message\message_id_store.py: 22%

83 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 

5from __future__ import annotations 

6 

7from typing import NoReturn 

8 

9from pylint.exceptions import InvalidMessageError, UnknownMessageError 

10 

11 

12class MessageIdStore: 

13 

14 """The MessageIdStore store MessageId and make sure that there is a 1-1 relation between msgid and symbol.""" 

15 

16 def __init__(self) -> None: 

17 self.__msgid_to_symbol: dict[str, str] = {} 

18 self.__symbol_to_msgid: dict[str, str] = {} 

19 self.__old_names: dict[str, list[str]] = {} 

20 self.__active_msgids: dict[str, list[str]] = {} 

21 

22 def __len__(self) -> int: 

23 return len(self.__msgid_to_symbol) 

24 

25 def __repr__(self) -> str: 

26 result = "MessageIdStore: [\n" 

27 for msgid, symbol in self.__msgid_to_symbol.items(): 

28 result += f" - {msgid} ({symbol})\n" 

29 result += "]" 

30 return result 

31 

32 def get_symbol(self, msgid: str) -> str: 

33 try: 

34 return self.__msgid_to_symbol[msgid.upper()] 

35 except KeyError as e: 

36 msg = f"'{msgid}' is not stored in the message store." 

37 raise UnknownMessageError(msg) from e 

38 

39 def get_msgid(self, symbol: str) -> str: 

40 try: 

41 return self.__symbol_to_msgid[symbol] 

42 except KeyError as e: 

43 msg = f"'{symbol}' is not stored in the message store." 

44 raise UnknownMessageError(msg) from e 

45 

46 def register_message_definition( 

47 self, msgid: str, symbol: str, old_names: list[tuple[str, str]] 

48 ) -> None: 

49 self.check_msgid_and_symbol(msgid, symbol) 

50 self.add_msgid_and_symbol(msgid, symbol) 

51 for old_msgid, old_symbol in old_names: 

52 self.check_msgid_and_symbol(old_msgid, old_symbol) 

53 self.add_legacy_msgid_and_symbol(old_msgid, old_symbol, msgid) 

54 

55 def add_msgid_and_symbol(self, msgid: str, symbol: str) -> None: 

56 """Add valid message id. 

57 

58 There is a little duplication with add_legacy_msgid_and_symbol to avoid a function call, 

59 this is called a lot at initialization. 

60 """ 

61 self.__msgid_to_symbol[msgid] = symbol 

62 self.__symbol_to_msgid[symbol] = msgid 

63 

64 def add_legacy_msgid_and_symbol( 

65 self, msgid: str, symbol: str, new_msgid: str 

66 ) -> None: 

67 """Add valid legacy message id. 

68 

69 There is a little duplication with add_msgid_and_symbol to avoid a function call, 

70 this is called a lot at initialization. 

71 """ 

72 self.__msgid_to_symbol[msgid] = symbol 

73 self.__symbol_to_msgid[symbol] = msgid 

74 existing_old_names = self.__old_names.get(msgid, []) 

75 existing_old_names.append(new_msgid) 

76 self.__old_names[msgid] = existing_old_names 

77 

78 def check_msgid_and_symbol(self, msgid: str, symbol: str) -> None: 

79 existing_msgid: str | None = self.__symbol_to_msgid.get(symbol) 

80 existing_symbol: str | None = self.__msgid_to_symbol.get(msgid) 

81 if existing_symbol is None and existing_msgid is None: 

82 return # both symbol and msgid are usable 

83 if existing_msgid is not None: 

84 if existing_msgid != msgid: 

85 self._raise_duplicate_msgid(symbol, msgid, existing_msgid) 

86 if existing_symbol and existing_symbol != symbol: 

87 # See https://github.com/python/mypy/issues/10559 

88 self._raise_duplicate_symbol(msgid, symbol, existing_symbol) 

89 

90 @staticmethod 

91 def _raise_duplicate_symbol(msgid: str, symbol: str, other_symbol: str) -> NoReturn: 

92 """Raise an error when a symbol is duplicated.""" 

93 symbols = [symbol, other_symbol] 

94 symbols.sort() 

95 error_message = f"Message id '{msgid}' cannot have both " 

96 error_message += f"'{symbols[0]}' and '{symbols[1]}' as symbolic name." 

97 raise InvalidMessageError(error_message) 

98 

99 @staticmethod 

100 def _raise_duplicate_msgid(symbol: str, msgid: str, other_msgid: str) -> NoReturn: 

101 """Raise an error when a msgid is duplicated.""" 

102 msgids = [msgid, other_msgid] 

103 msgids.sort() 

104 error_message = ( 

105 f"Message symbol '{symbol}' cannot be used for " 

106 f"'{msgids[0]}' and '{msgids[1]}' at the same time." 

107 f" If you're creating an 'old_names' use 'old-{symbol}' as the old symbol." 

108 ) 

109 raise InvalidMessageError(error_message) 

110 

111 def get_active_msgids(self, msgid_or_symbol: str) -> list[str]: 

112 """Return msgids but the input can be a symbol. 

113 

114 self.__active_msgids is used to implement a primitive cache for this function. 

115 """ 

116 try: 

117 return self.__active_msgids[msgid_or_symbol] 

118 except KeyError: 

119 pass 

120 

121 # If we don't have a cached value yet we compute it 

122 msgid: str | None 

123 if msgid_or_symbol[1:].isdigit(): 

124 # Only msgid can have a digit as second letter 

125 msgid = msgid_or_symbol.upper() 

126 symbol = self.__msgid_to_symbol.get(msgid) 

127 else: 

128 msgid = self.__symbol_to_msgid.get(msgid_or_symbol) 

129 symbol = msgid_or_symbol 

130 if not msgid or not symbol: 

131 error_msg = f"No such message id or symbol '{msgid_or_symbol}'." 

132 raise UnknownMessageError(error_msg) 

133 ids = self.__old_names.get(msgid, [msgid]) 

134 

135 # Add to cache 

136 self.__active_msgids[msgid_or_symbol] = ids 

137 return ids