1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
from typing import NoReturn
from pylint.exceptions import (
DeletedMessageError,
InvalidMessageError,
MessageBecameExtensionError,
UnknownMessageError,
)
from pylint.message._deleted_message_ids import (
is_deleted_msgid,
is_deleted_symbol,
is_moved_msgid,
is_moved_symbol,
)
class MessageIdStore:
"""The MessageIdStore store MessageId and make sure that there is a 1-1 relation
between msgid and symbol.
"""
def __init__(self) -> None:
self.__msgid_to_symbol: dict[str, str] = {}
self.__symbol_to_msgid: dict[str, str] = {}
self.__old_names: dict[str, list[str]] = {}
self.__active_msgids: dict[str, list[str]] = {}
def __len__(self) -> int:
return len(self.__msgid_to_symbol)
def __repr__(self) -> str:
result = "MessageIdStore: [\n"
for msgid, symbol in self.__msgid_to_symbol.items():
result += f" - {msgid} ({symbol})\n"
result += "]"
return result
def get_symbol(self, msgid: str) -> str:
try:
return self.__msgid_to_symbol[msgid.upper()]
except KeyError as e:
msg = f"'{msgid}' is not stored in the message store."
raise UnknownMessageError(msg) from e
def get_msgid(self, symbol: str) -> str:
try:
return self.__symbol_to_msgid[symbol]
except KeyError as e:
msg = f"'{symbol}' is not stored in the message store."
raise UnknownMessageError(msg) from e
def register_message_definition(
self, msgid: str, symbol: str, old_names: list[tuple[str, str]]
) -> None:
self.check_msgid_and_symbol(msgid, symbol)
self.add_msgid_and_symbol(msgid, symbol)
for old_msgid, old_symbol in old_names:
self.check_msgid_and_symbol(old_msgid, old_symbol)
self.add_legacy_msgid_and_symbol(old_msgid, old_symbol, msgid)
def add_msgid_and_symbol(self, msgid: str, symbol: str) -> None:
"""Add valid message id.
There is a little duplication with add_legacy_msgid_and_symbol to avoid a function call,
this is called a lot at initialization.
"""
self.__msgid_to_symbol[msgid] = symbol
self.__symbol_to_msgid[symbol] = msgid
def add_legacy_msgid_and_symbol(
self, msgid: str, symbol: str, new_msgid: str
) -> None:
"""Add valid legacy message id.
There is a little duplication with add_msgid_and_symbol to avoid a function call,
this is called a lot at initialization.
"""
self.__msgid_to_symbol[msgid] = symbol
self.__symbol_to_msgid[symbol] = msgid
existing_old_names = self.__old_names.get(msgid, [])
existing_old_names.append(new_msgid)
self.__old_names[msgid] = existing_old_names
def check_msgid_and_symbol(self, msgid: str, symbol: str) -> None:
existing_msgid: str | None = self.__symbol_to_msgid.get(symbol)
existing_symbol: str | None = self.__msgid_to_symbol.get(msgid)
if existing_symbol is None and existing_msgid is None:
return # both symbol and msgid are usable
if existing_msgid is not None:
if existing_msgid != msgid:
self._raise_duplicate_msgid(symbol, msgid, existing_msgid)
if existing_symbol and existing_symbol != symbol:
# See https://github.com/python/mypy/issues/10559
self._raise_duplicate_symbol(msgid, symbol, existing_symbol)
@staticmethod
def _raise_duplicate_symbol(msgid: str, symbol: str, other_symbol: str) -> NoReturn:
"""Raise an error when a symbol is duplicated."""
symbols = [symbol, other_symbol]
symbols.sort()
error_message = f"Message id '{msgid}' cannot have both "
error_message += f"'{symbols[0]}' and '{symbols[1]}' as symbolic name."
raise InvalidMessageError(error_message)
@staticmethod
def _raise_duplicate_msgid(symbol: str, msgid: str, other_msgid: str) -> NoReturn:
"""Raise an error when a msgid is duplicated."""
msgids = [msgid, other_msgid]
msgids.sort()
error_message = (
f"Message symbol '{symbol}' cannot be used for "
f"'{msgids[0]}' and '{msgids[1]}' at the same time."
f" If you're creating an 'old_names' use 'old-{symbol}' as the old symbol."
)
raise InvalidMessageError(error_message)
def get_active_msgids(self, msgid_or_symbol: str) -> list[str]:
"""Return msgids but the input can be a symbol.
self.__active_msgids is used to implement a primitive cache for this function.
"""
try:
return self.__active_msgids[msgid_or_symbol]
except KeyError:
pass
# If we don't have a cached value yet we compute it
msgid: str | None
deletion_reason = None
moved_reason = None
if msgid_or_symbol[1:].isdigit():
# Only msgid can have a digit as second letter
msgid = msgid_or_symbol.upper()
symbol = self.__msgid_to_symbol.get(msgid)
if not symbol:
deletion_reason = is_deleted_msgid(msgid)
if deletion_reason is None:
moved_reason = is_moved_msgid(msgid)
else:
symbol = msgid_or_symbol
msgid = self.__symbol_to_msgid.get(msgid_or_symbol)
if not msgid:
deletion_reason = is_deleted_symbol(symbol)
if deletion_reason is None:
moved_reason = is_moved_symbol(symbol)
if not msgid or not symbol:
if deletion_reason is not None:
raise DeletedMessageError(msgid_or_symbol, deletion_reason)
if moved_reason is not None:
raise MessageBecameExtensionError(msgid_or_symbol, moved_reason)
error_msg = f"No such message id or symbol '{msgid_or_symbol}'."
raise UnknownMessageError(error_msg)
ids = self.__old_names.get(msgid, [msgid])
# Add to cache
self.__active_msgids[msgid_or_symbol] = ids
return ids
|