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
« 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
5from __future__ import annotations
7from typing import NoReturn
9from pylint.exceptions import InvalidMessageError, UnknownMessageError
12class MessageIdStore:
14 """The MessageIdStore store MessageId and make sure that there is a 1-1 relation between msgid and symbol."""
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]] = {}
22 def __len__(self) -> int:
23 return len(self.__msgid_to_symbol)
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
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
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
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)
55 def add_msgid_and_symbol(self, msgid: str, symbol: str) -> None:
56 """Add valid message id.
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
64 def add_legacy_msgid_and_symbol(
65 self, msgid: str, symbol: str, new_msgid: str
66 ) -> None:
67 """Add valid legacy message id.
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
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)
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)
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)
111 def get_active_msgids(self, msgid_or_symbol: str) -> list[str]:
112 """Return msgids but the input can be a symbol.
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
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])
135 # Add to cache
136 self.__active_msgids[msgid_or_symbol] = ids
137 return ids