|
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