Coverage for C:\Repos\ekr-pylint\pylint\message\message_definition_store.py: 34%

62 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 

7import collections 

8import functools 

9from collections.abc import Sequence, ValuesView 

10from typing import TYPE_CHECKING 

11 

12from pylint.exceptions import UnknownMessageError 

13from pylint.message.message_definition import MessageDefinition 

14from pylint.message.message_id_store import MessageIdStore 

15 

16if TYPE_CHECKING: 

17 from pylint.checkers import BaseChecker 

18 

19 

20class MessageDefinitionStore: 

21 

22 """The messages store knows information about every possible message definition but has 

23 no particular state during analysis. 

24 """ 

25 

26 def __init__(self) -> None: 

27 self.message_id_store: MessageIdStore = MessageIdStore() 

28 # Primary registry for all active messages definitions. 

29 # It contains the 1:1 mapping from msgid to MessageDefinition. 

30 # Keys are msgid, values are MessageDefinition 

31 self._messages_definitions: dict[str, MessageDefinition] = {} 

32 # MessageDefinition kept by category 

33 self._msgs_by_category: dict[str, list[str]] = collections.defaultdict(list) 

34 

35 @property 

36 def messages(self) -> ValuesView[MessageDefinition]: 

37 """The list of all active messages.""" 

38 return self._messages_definitions.values() 

39 

40 def register_messages_from_checker(self, checker: BaseChecker) -> None: 

41 """Register all messages definitions from a checker.""" 

42 checker.check_consistency() 

43 for message in checker.messages: 

44 self.register_message(message) 

45 

46 def register_message(self, message: MessageDefinition) -> None: 

47 """Register a MessageDefinition with consistency in mind.""" 

48 self.message_id_store.register_message_definition( 

49 message.msgid, message.symbol, message.old_names 

50 ) 

51 self._messages_definitions[message.msgid] = message 

52 self._msgs_by_category[message.msgid[0]].append(message.msgid) 

53 

54 # Since MessageDefinitionStore is only initialized once 

55 # and the arguments are relatively small in size we do not run the 

56 # risk of creating a large memory leak. 

57 # See discussion in: https://github.com/PyCQA/pylint/pull/5673 

58 @functools.lru_cache(maxsize=None) # pylint: disable=method-cache-max-size-none 

59 def get_message_definitions(self, msgid_or_symbol: str) -> list[MessageDefinition]: 

60 """Returns the Message definition for either a numeric or symbolic id. 

61 

62 The cache has no limit as its size will likely stay minimal. For each message we store 

63 about 1000 characters, so even if we would have 1000 messages the cache would only 

64 take up ~= 1 Mb. 

65 """ 

66 return [ 

67 self._messages_definitions[m] 

68 for m in self.message_id_store.get_active_msgids(msgid_or_symbol) 

69 ] 

70 

71 def get_msg_display_string(self, msgid_or_symbol: str) -> str: 

72 """Generates a user-consumable representation of a message.""" 

73 message_definitions = self.get_message_definitions(msgid_or_symbol) 

74 if len(message_definitions) == 1: 

75 return repr(message_definitions[0].symbol) 

76 return repr([md.symbol for md in message_definitions]) 

77 

78 def help_message(self, msgids_or_symbols: Sequence[str]) -> None: 

79 """Display help messages for the given message identifiers.""" 

80 for msgids_or_symbol in msgids_or_symbols: 

81 try: 

82 for message_definition in self.get_message_definitions( 

83 msgids_or_symbol 

84 ): 

85 print(message_definition.format_help(checkerref=True)) 

86 print("") 

87 except UnknownMessageError as ex: 

88 print(ex) 

89 print("") 

90 continue 

91 

92 def list_messages(self) -> None: 

93 """Output full messages list documentation in ReST format.""" 

94 emittable, non_emittable = self.find_emittable_messages() 

95 print("Emittable messages with current interpreter:") 

96 for msg in emittable: 

97 print(msg.format_help(checkerref=False)) 

98 print("\nNon-emittable messages with current interpreter:") 

99 for msg in non_emittable: 

100 print(msg.format_help(checkerref=False)) 

101 print("") 

102 

103 def find_emittable_messages( 

104 self, 

105 ) -> tuple[list[MessageDefinition], list[MessageDefinition]]: 

106 """Finds all emittable and non-emittable messages.""" 

107 messages = sorted(self._messages_definitions.values(), key=lambda m: m.msgid) 

108 emittable = [] 

109 non_emittable = [] 

110 for message in messages: 

111 if message.may_be_emitted(): 

112 emittable.append(message) 

113 else: 

114 non_emittable.append(message) 

115 return emittable, non_emittable