actual = self.get_suggestion(A(), 'blech')
self.assertNotIn("Did you mean", actual)
+ def test_suggestions_not_normalized(self):
+ class A:
+ analization = None
+ fiⁿₐˡᵢᶻₐᵗᵢᵒₙ = None
+
+ suggestion = self.get_suggestion(A(), 'fiⁿₐˡᵢᶻₐᵗᵢᵒₙ')
+ self.assertIn("'finalization'", suggestion)
+ self.assertNotIn("analization", suggestion)
+
+ class B:
+ attr_a = None
+ attr_µ = None # attr_\xb5
+
+ suggestion = self.get_suggestion(B(), 'attr_\xb5')
+ self.assertIn("'attr_\u03bc'", suggestion)
+ self.assertIn(r"'attr_\u03bc'", suggestion)
+ self.assertNotIn("attr_a", suggestion)
+
class GetattrSuggestionTests(BaseSuggestionTests):
def test_suggestions_no_args(self):
actual = self.get_suggestion(instance.foo)
self.assertIn("self.blech", actual)
+ def test_name_error_with_instance_not_normalized(self):
+ class A:
+ def __init__(self):
+ self.fiⁿₐˡᵢᶻₐᵗᵢᵒₙ = None
+ def foo(self):
+ analization = 1
+ x = fiⁿₐˡᵢᶻₐᵗᵢᵒₙ
+
+ instance = A()
+ actual = self.get_suggestion(instance.foo)
+ self.assertIn("self.finalization", actual)
+ self.assertNotIn("fiⁿₐˡᵢᶻₐᵗᵢᵒₙ", actual)
+ self.assertNotIn("analization", actual)
+
+ class B:
+ def __init__(self):
+ self.attr_µ = None # attr_\xb5
+ def foo(self):
+ attr_a = 1
+ x = attr_µ # attr_\xb5
+
+ instance = B()
+ actual = self.get_suggestion(instance.foo)
+ self.assertIn("self.attr_\u03bc", actual)
+ self.assertIn(r"self.attr_\u03bc", actual)
+ self.assertNotIn("attr_\xb5", actual)
+ self.assertNotIn("attr_a", actual)
+
def test_unbound_local_error_with_instance(self):
class A:
def __init__(self):
wrong_name = getattr(exc_value, "name_from", None)
suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name)
if suggestion:
- self._str += f". Did you mean: '{suggestion}'?"
+ if suggestion.isascii():
+ self._str += f". Did you mean: '{suggestion}'?"
+ else:
+ self._str += f". Did you mean: '{suggestion}' ({suggestion!a})?"
elif exc_type and issubclass(exc_type, ModuleNotFoundError):
module_name = getattr(exc_value, "name", None)
if module_name in sys.stdlib_module_names:
wrong_name = getattr(exc_value, "name", None)
suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name)
if suggestion:
- self._str += f". Did you mean: '{suggestion}'?"
+ if suggestion.isascii():
+ self._str += f". Did you mean: '{suggestion}'?"
+ else:
+ self._str += f". Did you mean: '{suggestion}' ({suggestion!a})?"
if issubclass(exc_type, NameError):
wrong_name = getattr(exc_value, "name", None)
if wrong_name is not None and wrong_name in sys.stdlib_module_names:
def _compute_suggestion_error(exc_value, tb, wrong_name):
if wrong_name is None or not isinstance(wrong_name, str):
return None
+ not_normalized = False
+ if not wrong_name.isascii():
+ from unicodedata import normalize
+ normalized_name = normalize('NFKC', wrong_name)
+ if normalized_name != wrong_name:
+ not_normalized = True
+ wrong_name = normalized_name
if isinstance(exc_value, AttributeError):
obj = exc_value.obj
try:
+ list(frame.f_builtins)
)
d = [x for x in d if isinstance(x, str)]
+ if not_normalized and wrong_name in d:
+ return wrong_name
# Check first if we are in a method and the instance
# has the wrong name as attribute
if has_wrong_name:
return f"self.{wrong_name}"
+ if not_normalized and wrong_name in d:
+ return wrong_name
try:
import _suggestions
except ImportError: