]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-90095: Make .pdbrc work properly and add some reasonable tests (#110496)
authorTian Gao <gaogaotiantian@hotmail.com>
Mon, 11 Mar 2024 21:27:00 +0000 (14:27 -0700)
committerGitHub <noreply@github.com>
Mon, 11 Mar 2024 21:27:00 +0000 (21:27 +0000)
Lib/pdb.py
Lib/test/test_pdb.py
Misc/NEWS.d/next/Library/2023-10-07-06-15-13.gh-issue-90095.gWn1ka.rst [new file with mode: 0644]

index 519c1ccd5640a130ee6c26d32cfe84d29ddbb391..f4d19386703de0397d8f6e1a20a2369655ee9d09 100755 (executable)
@@ -363,26 +363,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
                 self._chained_exceptions[self._chained_exception_index],
             )
 
-        return self.execRcLines()
-
-    # Can be executed earlier than 'setup' if desired
-    def execRcLines(self):
-        if not self.rcLines:
-            return
-        # local copy because of recursion
-        rcLines = self.rcLines
-        rcLines.reverse()
-        # execute every line only once
-        self.rcLines = []
-        while rcLines:
-            line = rcLines.pop().strip()
-            if line and line[0] != '#':
-                if self.onecmd(line):
-                    # if onecmd returns True, the command wants to exit
-                    # from the interaction, save leftover rc lines
-                    # to execute before next interaction
-                    self.rcLines += reversed(rcLines)
-                    return True
+        if self.rcLines:
+            self.cmdqueue = self.rcLines
+            self.rcLines = []
 
     # Override Bdb methods
 
@@ -571,12 +554,10 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         if isinstance(tb_or_exc, BaseException):
             assert tb is not None, "main exception must have a traceback"
         with self._hold_exceptions(_chained_exceptions):
-            if self.setup(frame, tb):
-                # no interaction desired at this time (happens if .pdbrc contains
-                # a command like "continue")
-                self.forget()
-                return
-            self.print_stack_entry(self.stack[self.curindex])
+            self.setup(frame, tb)
+            # if we have more commands to process, do not show the stack entry
+            if not self.cmdqueue:
+                self.print_stack_entry(self.stack[self.curindex])
             self._cmdloop()
             self.forget()
 
@@ -712,7 +693,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
             if marker >= 0:
                 # queue up everything after marker
                 next = line[marker+2:].lstrip()
-                self.cmdqueue.append(next)
+                self.cmdqueue.insert(0, next)
                 line = line[:marker].rstrip()
 
         # Replace all the convenience variables
@@ -737,13 +718,12 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         """Handles one command line during command list definition."""
         cmd, arg, line = self.parseline(line)
         if not cmd:
-            return
+            return False
         if cmd == 'silent':
             self.commands_silent[self.commands_bnum] = True
-            return # continue to handle other cmd def in the cmd list
+            return False  # continue to handle other cmd def in the cmd list
         elif cmd == 'end':
-            self.cmdqueue = []
-            return 1 # end of cmd list
+            return True  # end of cmd list
         cmdlist = self.commands[self.commands_bnum]
         if arg:
             cmdlist.append(cmd+' '+arg)
@@ -757,9 +737,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         # one of the resuming commands
         if func.__name__ in self.commands_resuming:
             self.commands_doprompt[self.commands_bnum] = False
-            self.cmdqueue = []
-            return 1
-        return
+            return True
+        return False
 
     # interface abstraction functions
 
index 3dd275caf43200ee0f4742f7b9392823087a5140..44728542787423ebf00b73a44ea325c33802decb 100644 (file)
@@ -2618,13 +2618,29 @@ class PdbTestCase(unittest.TestCase):
 
     def run_pdb_script(self, script, commands,
                        expected_returncode=0,
-                       extra_env=None):
+                       extra_env=None,
+                       pdbrc=None,
+                       remove_home=False):
         """Run 'script' lines with pdb and the pdb 'commands'."""
         filename = 'main.py'
         with open(filename, 'w') as f:
             f.write(textwrap.dedent(script))
+
+        if pdbrc is not None:
+            with open('.pdbrc', 'w') as f:
+                f.write(textwrap.dedent(pdbrc))
+            self.addCleanup(os_helper.unlink, '.pdbrc')
         self.addCleanup(os_helper.unlink, filename)
-        return self._run_pdb([filename], commands, expected_returncode, extra_env)
+
+        homesave = None
+        if remove_home:
+            homesave = os.environ.pop('HOME', None)
+        try:
+            stdout, stderr = self._run_pdb([filename], commands, expected_returncode, extra_env)
+        finally:
+            if homesave is not None:
+                os.environ['HOME'] = homesave
+        return stdout, stderr
 
     def run_pdb_module(self, script, commands):
         """Runs the script code as part of a module"""
@@ -2904,37 +2920,80 @@ def bœr():
         self.assertRegex(res, "Restarting .* with arguments:\na b c")
         self.assertRegex(res, "Restarting .* with arguments:\nd e f")
 
-    def test_readrc_kwarg(self):
+    def test_pdbrc_basic(self):
         script = textwrap.dedent("""
-            import pdb; pdb.Pdb(readrc=False).set_trace()
+            a = 1
+            b = 2
+        """)
 
-            print('hello')
+        pdbrc = textwrap.dedent("""
+            # Comments should be fine
+            n
+            p f"{a+8=}"
         """)
 
-        save_home = os.environ.pop('HOME', None)
-        try:
-            with os_helper.temp_cwd():
-                with open('.pdbrc', 'w') as f:
-                    f.write("invalid\n")
-
-                with open('main.py', 'w') as f:
-                    f.write(script)
-
-                cmd = [sys.executable, 'main.py']
-                proc = subprocess.Popen(
-                    cmd,
-                    stdout=subprocess.PIPE,
-                    stdin=subprocess.PIPE,
-                    stderr=subprocess.PIPE,
-                )
-                with proc:
-                    stdout, stderr = proc.communicate(b'q\n')
-                    self.assertNotIn(b"NameError: name 'invalid' is not defined",
-                                  stdout)
+        stdout, stderr = self.run_pdb_script(script, 'q\n', pdbrc=pdbrc, remove_home=True)
+        self.assertIn("a+8=9", stdout)
 
-        finally:
-            if save_home is not None:
-                os.environ['HOME'] = save_home
+    def test_pdbrc_alias(self):
+        script = textwrap.dedent("""
+            class A:
+                def __init__(self):
+                    self.attr = 1
+            a = A()
+            b = 2
+        """)
+
+        pdbrc = textwrap.dedent("""
+            alias pi for k in %1.__dict__.keys(): print(f"%1.{k} = {%1.__dict__[k]}")
+            until 6
+            pi a
+        """)
+
+        stdout, stderr = self.run_pdb_script(script, 'q\n', pdbrc=pdbrc, remove_home=True)
+        self.assertIn("a.attr = 1", stdout)
+
+    def test_pdbrc_semicolon(self):
+        script = textwrap.dedent("""
+            class A:
+                def __init__(self):
+                    self.attr = 1
+            a = A()
+            b = 2
+        """)
+
+        pdbrc = textwrap.dedent("""
+            b 5;;c;;n
+        """)
+
+        stdout, stderr = self.run_pdb_script(script, 'q\n', pdbrc=pdbrc, remove_home=True)
+        self.assertIn("-> b = 2", stdout)
+
+    def test_pdbrc_commands(self):
+        script = textwrap.dedent("""
+            class A:
+                def __init__(self):
+                    self.attr = 1
+            a = A()
+            b = 2
+        """)
+
+        pdbrc = textwrap.dedent("""
+            b 6
+            commands 1 ;; p a;; end
+            c
+        """)
+
+        stdout, stderr = self.run_pdb_script(script, 'q\n', pdbrc=pdbrc, remove_home=True)
+        self.assertIn("<__main__.A object at", stdout)
+
+    def test_readrc_kwarg(self):
+        script = textwrap.dedent("""
+            print('hello')
+        """)
+
+        stdout, stderr = self.run_pdb_script(script, 'q\n', pdbrc='invalid', remove_home=True)
+        self.assertIn("NameError: name 'invalid' is not defined", stdout)
 
     def test_readrc_homedir(self):
         save_home = os.environ.pop("HOME", None)
@@ -2949,40 +3008,6 @@ def bœr():
                 if save_home is not None:
                     os.environ["HOME"] = save_home
 
-    def test_read_pdbrc_with_ascii_encoding(self):
-        script = textwrap.dedent("""
-            import pdb; pdb.Pdb().set_trace()
-            print('hello')
-        """)
-        save_home = os.environ.pop('HOME', None)
-        try:
-            with os_helper.temp_cwd():
-                with open('.pdbrc', 'w', encoding='utf-8') as f:
-                    f.write("Fran\u00E7ais")
-
-                with open('main.py', 'w', encoding='utf-8') as f:
-                    f.write(script)
-
-                cmd = [sys.executable, 'main.py']
-                env = {'PYTHONIOENCODING': 'ascii'}
-                if sys.platform == 'win32':
-                    env['PYTHONLEGACYWINDOWSSTDIO'] = 'non-empty-string'
-                proc = subprocess.Popen(
-                    cmd,
-                    stdout=subprocess.PIPE,
-                    stdin=subprocess.PIPE,
-                    stderr=subprocess.PIPE,
-                    env={**os.environ, **env}
-                )
-                with proc:
-                    stdout, stderr = proc.communicate(b'c\n')
-                    self.assertIn(b"UnicodeEncodeError: \'ascii\' codec can\'t encode character "
-                                  b"\'\\xe7\' in position 21: ordinal not in range(128)", stderr)
-
-        finally:
-            if save_home is not None:
-                os.environ['HOME'] = save_home
-
     def test_header(self):
         stdout = StringIO()
         header = 'Nobody expects... blah, blah, blah'
diff --git a/Misc/NEWS.d/next/Library/2023-10-07-06-15-13.gh-issue-90095.gWn1ka.rst b/Misc/NEWS.d/next/Library/2023-10-07-06-15-13.gh-issue-90095.gWn1ka.rst
new file mode 100644 (file)
index 0000000..d71442e
--- /dev/null
@@ -0,0 +1 @@
+Make .pdbrc and -c work with any valid pdb commands.