Repository: spyder-ide/spyder
Pull request: PR: Only go to line in editor when Pdb changes line
<!--- Make sure to read the Contributing Guidelines:                   --->
<!--- https://github.com/spyder-ide/spyder/blob/master/CONTRIBUTING.md --->
<!--- and follow PEP 8, PEP 257 and Spyder's code style:               --->
<!--- https://github.com/spyder-ide/spyder/wiki/Dev:-Coding-Style      --->

## Description of Changes
Notify spyder should not move back to a position it already moved to

* [ ] Wrote at least one-line docstrings (for any new functions)
* [x] Added unit test(s) covering the changes (if testable)
<!--- Remember that an image/animation is worth a thousand words! --->
* [ ] Included a screenshot or animation (if affecting the UI, see [Licecap](https://www.cockos.com/licecap/))


<!--- Explain what you've done and why --->




### Issue(s) Resolved

<!--- List the issue(s) below, in the form "Fixes #1234"; one per line --->

Fixes #10851
Depends on spyder-ide/spyder-kernels#185

### Affirmation

By submitting this Pull Request or typing my (user)name below,
I affirm the [Developer Certificate of Origin](https://developercertificate.org)
with respect to all commits and content included in this PR,
and understand I am releasing the same under Spyder's MIT (Expat) license.

<!--- TYPE YOUR USER/NAME AFTER THE FOLLOWING: --->
I certify the above statement is true and correct: impact27

<!--- Thanks for your help making Spyder better for everyone! --->


Problem statement:
Pdb switch code editor tabs without reason
## Description

### What steps will reproduce the problem?

<!--- You can use Markdown here --->

1. create a file with `print(0)`
2. Debug this file and step into `print`. This brings you to `iostream.py`
3. Click on the original file tab in the editor
4. type in the debugger: `test = 0`

This will bring the editor back to `iostream.py`. This is desirable when stepping through the code but not on any input.

## Versions

* Spyder version: 4.0.0.dev0 6d435b3e8
* Python version: 3.7.5
* Qt version: 5.12.6
* PyQt5 version: 5.12.3
* Operating System: Darwin 19.0.0

### Dependencies

```
cloudpickle >=0.5.0          :  1.2.2 (OK)
pygments >=2.0               :  2.4.2 (OK)
qtconsole >=4.6.0            :  4.7.0.dev0 (OK)
nbconvert >=4.0              :  5.6.1 (OK)
sphinx >=0.6.6               :  2.2.1 (OK)
pylint >=0.25                :  2.4.4 (OK)
psutil >=0.3                 :  5.6.5 (OK)
qtawesome >=0.5.7            :  0.6.0 (OK)
qtpy >=1.5.0                 :  1.9.0 (OK)
pickleshare >=0.4            :  0.7.5 (OK)
zmq >=17                     :  18.1.1 (OK)
chardet >=2.0.0              :  3.0.4 (OK)
numpydoc >=0.6.0             :  0.9.1 (OK)
spyder_kernels >=1.8.0;<2.0.0:  1.9.0.dev0 (OK)
qdarkstyle >=2.7             :  2.7 (OK)
atomicwrites >=1.2.0         :  1.3.0 (OK)
diff_match_patch >=20181111  :  20181111 (OK)
intervaltree                 :  None (OK)
watchdog                     :  None (OK)
keyring                      :  None (OK)
pexpect >=4.4.0              :  4.7.0 (OK)
pympler                      :  None (OK)
sympy >=0.7.3                :  1.4 (OK)
cython >=0.21                :  None (NOK)
IPython >=4.0                :  7.9.0 (OK)
matplotlib >=2.0.0           :  3.1.2 (OK)
pandas >=0.13.1              :  0.25.3 (OK)
numpy >=1.7                  :  1.17.4 (OK)
scipy >=0.17.0               :  1.3.3 (OK)
pyls >=0.31.1;<0.32.0        :  0.31.1 (OK)
applaunchservices >=0.1.7    :  0.2.1 (OK)
```

Hints from issue discussion:

Commit message: Revert "Use PR 185"

This reverts commit 0dae67cb31914b1e7899e1a7efe1b25c4add9d4f.

Patch to review:
diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py
index 50a7947df0b..34e4fa3213e 100644
--- a/spyder/app/tests/test_mainwindow.py
+++ b/spyder/app/tests/test_mainwindow.py
@@ -2756,5 +2756,75 @@ def test_path_manager_updates_clients(qtbot, main_window, tmpdir):
     assert count >= 1
 
 
+@pytest.mark.slow
+@flaky(max_runs=3)
+@pytest.mark.skipif(sys.platform == 'darwin', reason="It times out on macOS")
+def test_pbd_step(main_window, qtbot, tmpdir):
+    """
+    Check that pdb notify spyder only moves when a new line is reached.
+    """
+    # Wait until the window is fully up
+    shell = main_window.ipyconsole.get_current_shellwidget()
+    qtbot.waitUntil(lambda: shell._prompt_html is not None,
+                    timeout=SHELL_TIMEOUT)
+    control = shell._control
+
+    # Write code to a file
+    code1 = ("def a():\n"
+             "    1/0")
+    code2 = ("from tmp import a\n"
+             "a()")
+    folder = tmpdir.join('tmp_folder')
+    test_file = folder.join('tmp.py')
+    test_file.write(code1, ensure=True)
+
+    test_file2 = folder.join('tmp2.py')
+    test_file2.write(code2)
+
+    # Run tmp2 and get an error
+    with qtbot.waitSignal(shell.executed):
+        shell.execute('runfile("' + str(test_file2).replace("\\", "/") +
+                      '", wdir="' + str(folder).replace("\\", "/") + '")')
+    assert '1/0' in control.toPlainText()
+
+    # Debug and enter first file
+    shell.execute('%debug')
+    qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+    qtbot.waitUntil(
+        lambda: osp.samefile(
+            main_window.editor.get_current_editor().filename,
+            str(test_file)))
+
+    # Go up and enter second file
+    qtbot.keyClick(control, 'u')
+    qtbot.keyClick(control, Qt.Key_Enter)
+    qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+    qtbot.waitUntil(
+        lambda: osp.samefile(
+            main_window.editor.get_current_editor().filename,
+            str(test_file2)))
+
+    # Go back to first file
+    editor_stack = main_window.editor.get_current_editorstack()
+    index = editor_stack.has_filename(str(test_file))
+    assert index is not None
+    editor_stack.set_stack_index(index)
+
+    assert osp.samefile(
+        main_window.editor.get_current_editor().filename,
+        str(test_file))
+
+    # Change frame but stay at the same place
+    qtbot.keyClicks(control, 'test = 0')
+    qtbot.keyClick(control, Qt.Key_Enter)
+    qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+    qtbot.wait(1000)
+
+    # Make sure we didn't move
+    assert osp.samefile(
+        main_window.editor.get_current_editor().filename,
+        str(test_file))
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/spyder/plugins/ipythonconsole/widgets/debugging.py b/spyder/plugins/ipythonconsole/widgets/debugging.py
index 558b074136b..0a7f0af6bde 100644
--- a/spyder/plugins/ipythonconsole/widgets/debugging.py
+++ b/spyder/plugins/ipythonconsole/widgets/debugging.py
@@ -67,6 +67,7 @@ def __init__(self, *args, **kwargs):
         self._pdb_history_index = len(self._pdb_history)
 
         self._tmp_reading = False
+        self._pdb_frame_loc = (None, None)
 
     def handle_debug_state(self, in_debug_loop):
         """Update the debug state."""
@@ -74,6 +75,7 @@ def handle_debug_state(self, in_debug_loop):
         # If debugging starts or stops, clear the input queue.
         self._pdb_input_queue = []
         self._pdb_line_num = 0
+        self._pdb_frame_loc = (None, None)
 
         # start/stop pdb history session
         if in_debug_loop:
@@ -165,7 +167,10 @@ def refresh_from_pdb(self, pdb_state):
         if 'step' in pdb_state and 'fname' in pdb_state['step']:
             fname = pdb_state['step']['fname']
             lineno = pdb_state['step']['lineno']
-            self.sig_pdb_step.emit(fname, lineno)
+            # Only step if the location changed
+            if (fname, lineno) != self._pdb_frame_loc:
+                self._pdb_frame_loc = (fname, lineno)
+                self.sig_pdb_step.emit(fname, lineno)
 
         if 'namespace_view' in pdb_state:
             self.set_namespace_view(pdb_state['namespace_view'])


Code review comments:
diff_hunk: @@ -2756,5 +2756,75 @@ def test_path_manager_updates_clients(qtbot, main_window, tmpdir):
     assert count >= 1
 
 
+@pytest.mark.slow
+@flaky(max_runs=3)
+@pytest.mark.skipif(sys.platform == 'darwin', reason="It times out on macOS")
+def test_pbd_step(main_window, qtbot, tmpdir):
+    """
+    Check that pdb notify spyder only moves when a new line is reached.
path: spyder/app/tests/test_mainwindow.py
text: @user1:
spyder -> Spyder