gh-130999: Avoid exiting the new REPL when there are non-string candidates for suggestions (gh-131001)

This commit is contained in:
devdanzin 2025-06-02 08:04:59 -03:00 committed by GitHub
parent 5f61cde80a
commit baccfdb3d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 48 additions and 2 deletions

View file

@ -1672,6 +1672,17 @@ class TestMain(ReplTestCase):
self.assertEqual(exit_code, 0)
self.assertNotIn("TypeError", output)
@force_not_colorized
def test_non_string_suggestion_candidates(self):
commands = ("import runpy\n"
"runpy._run_module_code('blech', {0: '', 'bluch': ''}, '')\n"
"exit()\n")
output, exit_code = self.run_repl(commands)
self.assertEqual(exit_code, 0)
self.assertNotIn("all elements in 'candidates' must be strings", output)
self.assertIn("bluch", output)
def test_readline_history_file(self):
# skip, if readline module is not available
readline = import_module('readline')

View file

@ -4188,6 +4188,15 @@ class SuggestionFormattingTestBase:
self.assertNotIn("blech", actual)
self.assertNotIn("oh no!", actual)
def test_attribute_error_with_non_string_candidates(self):
class T:
bluch = 1
instance = T()
instance.__dict__[0] = 1
actual = self.get_suggestion(instance, 'blich')
self.assertIn("bluch", actual)
def test_attribute_error_with_bad_name(self):
def raise_attribute_error_with_bad_name():
raise AttributeError(name=12, obj=23)
@ -4301,6 +4310,13 @@ class SuggestionFormattingTestBase:
self.assertIn("'_bluch'", self.get_import_from_suggestion(code, '_luch'))
self.assertNotIn("'_bluch'", self.get_import_from_suggestion(code, 'bluch'))
def test_import_from_suggestions_non_string(self):
modWithNonStringAttr = textwrap.dedent("""\
globals()[0] = 1
bluch = 1
""")
self.assertIn("'bluch'", self.get_import_from_suggestion(modWithNonStringAttr, 'blech'))
def test_import_from_suggestions_do_not_trigger_for_long_attributes(self):
code = "blech = None"
@ -4397,6 +4413,14 @@ class SuggestionFormattingTestBase:
actual = self.get_suggestion(func)
self.assertIn("'ZeroDivisionError'?", actual)
def test_name_error_suggestions_with_non_string_candidates(self):
def func():
abc = 1
globals()[0] = 1
abv
actual = self.get_suggestion(func)
self.assertIn("abc", actual)
def test_name_error_suggestions_do_not_trigger_for_long_names(self):
def func():
somethingverywronghehehehehehe = None

View file

@ -1595,7 +1595,11 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
if isinstance(exc_value, AttributeError):
obj = exc_value.obj
try:
d = dir(obj)
try:
d = dir(obj)
except TypeError: # Attributes are unsortable, e.g. int and str
d = list(obj.__class__.__dict__.keys()) + list(obj.__dict__.keys())
d = sorted([x for x in d if isinstance(x, str)])
hide_underscored = (wrong_name[:1] != '_')
if hide_underscored and tb is not None:
while tb.tb_next is not None:
@ -1610,7 +1614,11 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
elif isinstance(exc_value, ImportError):
try:
mod = __import__(exc_value.name)
d = dir(mod)
try:
d = dir(mod)
except TypeError: # Attributes are unsortable, e.g. int and str
d = list(mod.__dict__.keys())
d = sorted([x for x in d if isinstance(x, str)])
if wrong_name[:1] != '_':
d = [x for x in d if x[:1] != '_']
except Exception:
@ -1628,6 +1636,7 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
+ list(frame.f_globals)
+ list(frame.f_builtins)
)
d = [x for x in d if isinstance(x, str)]
# Check first if we are in a method and the instance
# has the wrong name as attribute

View file

@ -0,0 +1,2 @@
Avoid exiting the new REPL and offer suggestions even if there are non-string
candidates when errors occur.