|
13 | 13 | import shlex |
14 | 14 | import sys |
15 | 15 | from collections.abc import Callable, Iterable |
16 | | -from functools import cached_property, singledispatch |
| 16 | +from functools import cached_property, lru_cache, singledispatch |
17 | 17 | from re import Pattern |
18 | 18 | from typing import TYPE_CHECKING, Any, Literal, Union |
19 | 19 |
|
@@ -172,6 +172,7 @@ def _string_distance(seq1: str, seq2: str, seq1_length: int, seq2_length: int) - |
172 | 172 | return row[seq2_length - 1] |
173 | 173 |
|
174 | 174 |
|
| 175 | +@lru_cache(maxsize=256) |
175 | 176 | def _similar_names( |
176 | 177 | owner: SuccessfulInferenceResult, |
177 | 178 | attrname: str | None, |
@@ -214,26 +215,6 @@ def _similar_names( |
214 | 215 | return sorted(picked) |
215 | 216 |
|
216 | 217 |
|
217 | | -def _missing_member_hint( |
218 | | - owner: SuccessfulInferenceResult, |
219 | | - attrname: str | None, |
220 | | - distance_threshold: int, |
221 | | - max_choices: int, |
222 | | -) -> str: |
223 | | - names = _similar_names(owner, attrname, distance_threshold, max_choices) |
224 | | - if not names: |
225 | | - # No similar name. |
226 | | - return "" |
227 | | - |
228 | | - names = [repr(name) for name in names] |
229 | | - if len(names) == 1: |
230 | | - names_hint = ", ".join(names) |
231 | | - else: |
232 | | - names_hint = f"one of {', '.join(names[:-1])} or {names[-1]}" |
233 | | - |
234 | | - return f"; maybe {names_hint}?" |
235 | | - |
236 | | - |
237 | 218 | MSGS: dict[str, MessageDefinitionTuple] = { |
238 | 219 | "E1101": ( |
239 | 220 | "%s %r has no %r member%s", |
@@ -997,10 +978,6 @@ def open(self) -> None: |
997 | 978 | self._py310_plus = py_version >= (3, 10) |
998 | 979 | self._mixin_class_rgx = self.linter.config.mixin_class_rgx |
999 | 980 |
|
1000 | | - @cached_property |
1001 | | - def _suggestion_mode(self) -> bool: |
1002 | | - return self.linter.config.suggestion_mode # type: ignore[no-any-return] |
1003 | | - |
1004 | 981 | @cached_property |
1005 | 982 | def _compiled_generated_members(self) -> tuple[Pattern[str], ...]: |
1006 | 983 | # do this lazily since config not fully initialized in __init__ |
@@ -1211,24 +1188,24 @@ def _get_nomember_msgid_hint( |
1211 | 1188 | node: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr, |
1212 | 1189 | owner: SuccessfulInferenceResult, |
1213 | 1190 | ) -> tuple[Literal["c-extension-no-member", "no-member"], str]: |
1214 | | - suggestions_are_possible = self._suggestion_mode and isinstance( |
1215 | | - owner, nodes.Module |
| 1191 | + if _is_c_extension(owner): |
| 1192 | + return "c-extension-no-member", "" |
| 1193 | + if not self.linter.config.missing_member_hint: |
| 1194 | + return "no-member", "" |
| 1195 | + names = _similar_names( |
| 1196 | + owner, |
| 1197 | + node.attrname, |
| 1198 | + self.linter.config.missing_member_hint_distance, |
| 1199 | + self.linter.config.missing_member_max_choices, |
1216 | 1200 | ) |
1217 | | - if suggestions_are_possible and _is_c_extension(owner): |
1218 | | - msg = "c-extension-no-member" |
1219 | | - hint = "" |
| 1201 | + if not names: |
| 1202 | + return "no-member", "" |
| 1203 | + names = [repr(name) for name in names] |
| 1204 | + if len(names) == 1: |
| 1205 | + names_hint = names[0] |
1220 | 1206 | else: |
1221 | | - msg = "no-member" |
1222 | | - if self.linter.config.missing_member_hint: |
1223 | | - hint = _missing_member_hint( |
1224 | | - owner, |
1225 | | - node.attrname, |
1226 | | - self.linter.config.missing_member_hint_distance, |
1227 | | - self.linter.config.missing_member_max_choices, |
1228 | | - ) |
1229 | | - else: |
1230 | | - hint = "" |
1231 | | - return msg, hint # type: ignore[return-value] |
| 1207 | + names_hint = f"one of {', '.join(names[:-1])} or {names[-1]}" |
| 1208 | + return "no-member", f"; maybe {names_hint}?" |
1232 | 1209 |
|
1233 | 1210 | @only_required_for_messages( |
1234 | 1211 | "assignment-from-no-return", |
|
0 commit comments