]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-130999: Avoid exiting the new REPL when there are non-string candidates...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Mon, 2 Jun 2025 14:58:29 +0000 (16:58 +0200)
committerGitHub <noreply@github.com>
Mon, 2 Jun 2025 14:58:29 +0000 (16:58 +0200)
Co-authored-by: Ɓukasz Langa <lukasz@langa.pl>
Lib/test/test_pyrepl/test_pyrepl.py
Lib/test/test_traceback.py
Lib/traceback.py
Misc/NEWS.d/next/Library/2025-03-09-03-13-41.gh-issue-130999.tBRBVB.rst [new file with mode: 0644]

index 8661922cd3ec8c401c4f91582cb04db4a8d752fe..d20934612520c8dd98fa2819a0633cb07d86f4a3 100644 (file)
@@ -1342,6 +1342,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')
index eaa1a4fa4fc74fcadb17277009d5598d6ecb72bd..4b5dd3d25657315efb538268eb7f389b519278a2 100644 (file)
@@ -4176,6 +4176,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)
@@ -4211,8 +4220,8 @@ class SuggestionFormattingTestBase:
 
         return mod_name
 
-    def get_import_from_suggestion(self, mod_dict, name):
-        modname = self.make_module(mod_dict)
+    def get_import_from_suggestion(self, code, name):
+        modname = self.make_module(code)
 
         def callable():
             try:
@@ -4289,6 +4298,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"
 
@@ -4385,6 +4401,15 @@ 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
+            custom_globals = globals().copy()
+            custom_globals[0] = 1
+            print(eval("abv", custom_globals, locals()))
+        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
index a3b7de085701c6da1ae9d51543ed8f36f5e380f6..e70d358ca2be44a8c087205d5abe39ea36ef62df 100644 (file)
@@ -1490,7 +1490,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:
@@ -1505,7 +1509,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:
@@ -1523,6 +1531,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
diff --git a/Misc/NEWS.d/next/Library/2025-03-09-03-13-41.gh-issue-130999.tBRBVB.rst b/Misc/NEWS.d/next/Library/2025-03-09-03-13-41.gh-issue-130999.tBRBVB.rst
new file mode 100644 (file)
index 0000000..157522f
--- /dev/null
@@ -0,0 +1,2 @@
+Avoid exiting the new REPL and offer suggestions even if there are non-string
+candidates when errors occur.