|
3 | 3 | # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt |
4 | 4 | from __future__ import annotations |
5 | 5 |
|
| 6 | +import re |
| 7 | +from collections.abc import Callable |
6 | 8 | from pathlib import PurePosixPath |
7 | 9 |
|
8 | 10 | from pylint.reporters.json_reporter import OldJsonExport |
|
14 | 16 | from pylint.testutils._primer.primer_command import PrimerCommand |
15 | 17 |
|
16 | 18 | MAX_GITHUB_COMMENT_LENGTH = 65536 |
| 19 | +USELESS_SUPPRESSION_RE = re.compile(r"Useless suppression of '(.+)'") |
| 20 | + |
| 21 | + |
| 22 | +class _ClassifiedMessages: |
| 23 | + """All message categories for a single package, pre-formatted for display.""" |
| 24 | + |
| 25 | + __slots__ = ( |
| 26 | + "astroid_errors", |
| 27 | + "changed_messages", |
| 28 | + "fixed_false_positives", |
| 29 | + "missing_messages", |
| 30 | + "new_false_positives", |
| 31 | + "new_messages", |
| 32 | + ) |
| 33 | + |
| 34 | + def __init__( |
| 35 | + self, |
| 36 | + new_messages: list[OldJsonExport], |
| 37 | + missing_messages: list[OldJsonExport], |
| 38 | + changed_messages: list[ChangedMessage], |
| 39 | + source_link: Callable[[OldJsonExport], str], |
| 40 | + ) -> None: |
| 41 | + astroid_errors = 0 |
| 42 | + new_fp: list[OldJsonExport] = [] |
| 43 | + fixed_fp: list[OldJsonExport] = [] |
| 44 | + other_new: list[OldJsonExport] = [] |
| 45 | + for message in new_messages: |
| 46 | + if message["symbol"] == "astroid-error": |
| 47 | + astroid_errors += 1 |
| 48 | + elif USELESS_SUPPRESSION_RE.match(message["message"]): |
| 49 | + fixed_fp.append(message) |
| 50 | + elif message["symbol"] == "locally-disabled": |
| 51 | + # A new locally-disabled means a maintainer had to add a |
| 52 | + # suppression comment β we introduced a false positive. |
| 53 | + new_fp.append(message) |
| 54 | + else: |
| 55 | + other_new.append(message) |
| 56 | + |
| 57 | + def _format(msgs: list[OldJsonExport]) -> str: |
| 58 | + body = "" |
| 59 | + for count, msg in enumerate(msgs, 1): |
| 60 | + body += ( |
| 61 | + f"{count}) {msg['symbol']}:\n*{msg['message']}*\n" |
| 62 | + f"{source_link(msg)}\n" |
| 63 | + ) |
| 64 | + return body |
| 65 | + |
| 66 | + self.astroid_errors = astroid_errors |
| 67 | + self.fixed_false_positives = _format(fixed_fp) |
| 68 | + self.new_false_positives = _format(new_fp) |
| 69 | + self.new_messages = _format(other_new) |
| 70 | + self.missing_messages = _format(missing_messages) |
| 71 | + |
| 72 | + body = "" |
| 73 | + for count, (old, new) in enumerate(changed_messages, 1): |
| 74 | + body += ( |
| 75 | + f"{count}) [{new['symbol']}]({source_link(new)}):\n" |
| 76 | + f"{message_diff(old, new)}\n" |
| 77 | + ) |
| 78 | + self.changed_messages = body |
17 | 79 |
|
18 | 80 |
|
19 | 81 | class CompareCommand(PrimerCommand): |
@@ -70,58 +132,41 @@ def _details_section(title: str, body: str) -> str: |
70 | 132 | # Two line breaks required after <details> for links to work. |
71 | 133 | return f"{title}\n\n<details>\n\n{body}</details>\n\n" |
72 | 134 |
|
| 135 | + classified = _ClassifiedMessages( |
| 136 | + new_messages, missing_messages, changed_messages, _source_link |
| 137 | + ) |
73 | 138 | comment = f"\n**Effect on [{package}]({url}):**\n\n" |
74 | 139 |
|
75 | | - # -- Changed messages (same diagnostic, different details) ----------- |
76 | | - if changed_messages: |
77 | | - body = "" |
78 | | - for count, (old, new) in enumerate(changed_messages, 1): |
79 | | - body += ( |
80 | | - f"{count}) [{new['symbol']}]({_source_link(new)}):\n" |
81 | | - f"{message_diff(old, new)}\n" |
82 | | - ) |
| 140 | + if classified.changed_messages: |
83 | 141 | comment += _details_section( |
84 | | - "The following messages have been changed:", body |
| 142 | + "The following messages have been changed:", |
| 143 | + classified.changed_messages, |
85 | 144 | ) |
86 | | - |
87 | | - # -- New messages ---------------------------------------------------- |
88 | | - astroid_errors = 0 |
89 | | - new_non_astroid_messages = "" |
90 | | - count = 0 |
91 | | - for message in new_messages: |
92 | | - # Existing astroid errors may still show up as "new" because the |
93 | | - # timestamp in the message is slightly different. |
94 | | - if message["symbol"] == "astroid-error": |
95 | | - astroid_errors += 1 |
96 | | - else: |
97 | | - count += 1 |
98 | | - new_non_astroid_messages += ( |
99 | | - f"{count}) {message['symbol']}:\n*{message['message']}*\n" |
100 | | - f"{_source_link(message)}\n" |
101 | | - ) |
102 | | - |
103 | | - if astroid_errors: |
| 145 | + if classified.astroid_errors: |
104 | 146 | comment += ( |
105 | | - f'{astroid_errors} "astroid error(s)" were found. ' |
| 147 | + f'{classified.astroid_errors} "astroid error(s)" were found. ' |
106 | 148 | "Please open the GitHub Actions log to see what failed or crashed.\n\n" |
107 | 149 | ) |
108 | | - if new_non_astroid_messages: |
| 150 | + if classified.fixed_false_positives: |
109 | 151 | comment += _details_section( |
110 | | - "The following messages are now emitted:", new_non_astroid_messages |
| 152 | + "π The following false positives have been fixed:", |
| 153 | + classified.fixed_false_positives, |
111 | 154 | ) |
112 | | - |
113 | | - # -- Missing messages ------------------------------------------------ |
114 | | - if missing_messages: |
115 | | - body = "" |
116 | | - for count, message in enumerate(missing_messages, 1): |
117 | | - body += ( |
118 | | - f"{count}) {message['symbol']}:\n*{message['message']}*\n" |
119 | | - f"{_source_link(message)}\n" |
120 | | - ) |
| 155 | + if classified.new_false_positives: |
121 | 156 | comment += _details_section( |
122 | | - "The following messages are no longer emitted:", body |
| 157 | + "π The following false positives have been introduced:", |
| 158 | + classified.new_false_positives, |
| 159 | + ) |
| 160 | + if classified.new_messages: |
| 161 | + comment += _details_section( |
| 162 | + "The following messages are now emitted:", |
| 163 | + classified.new_messages, |
| 164 | + ) |
| 165 | + if classified.missing_messages: |
| 166 | + comment += _details_section( |
| 167 | + "The following messages are no longer emitted:", |
| 168 | + classified.missing_messages, |
123 | 169 | ) |
124 | | - |
125 | 170 | return comment |
126 | 171 |
|
127 | 172 | def _truncate_comment(self, comment: str) -> str: |
|
0 commit comments