Repository: spyder-ide/spyder

Issue:
Code completion doesn't work in the debugger
_From [mproel...@googlemail.com](https://code.google.com/u/105494983286078857315/) on 2010-08-12T04:07:56Z_

Hi,

when starting a debug session, and being in a function the code completition doesn't show up a local variable that you have defined. Example:

def test():
    print "hello world"

test()

run a debug session, when being at print ... type
(Pdb) !var = 13
(Pdb) v<tab> #var doesn't show up
(Pdb) var
13

For global variables it works fine (e.g. do the same at line test())

_Original issue: http://code.google.com/p/spyderlib/issues/detail?id=288_



Discussion:
_From [mproel...@googlemail.com](https://code.google.com/u/105494983286078857315/) on 2010-08-17T23:05:31Z_

The same with the following code:

import pdb

def function_1(number):
    local_variable = number
    pdb.set_trace()
    print local_variable

function_1(1)

-> print local_variable
(Pdb) lo<TAB> local_variable is not in the code completition list

_From [ccordoba12](https://code.google.com/u/ccordoba12/) on 2011-05-05T08:51:58Z_

Unfortunately, Pdb doesn't have code completion facilities, that's why Spyder couldn't get them either.

To solve this would require proper integration with ipdb, an enhanced Pdb with ipython capabilities.

**Summary:** Code completition doesn't show up new variables or self in debug mode  

_From [ccordoba12](https://code.google.com/u/ccordoba12/) on 2011-05-05T08:53:08Z_

issue #286 has been merged into this issue.

_From [ccordoba12](https://code.google.com/u/ccordoba12/) on 2011-05-05T14:12:01Z_

**Labels:** Debugger  

_From [ccordoba12](https://code.google.com/u/ccordoba12/) on 2011-05-15T19:29:50Z_

**Labels:** -Debugger Cat-Debugger  

I recently started to use ipdb in spyder and I think it is a great combination. However I do not see autocompletion of variable names in the ipdb session. Is this expected or not? 
Here is a discussion about it https://github.com/gotcha/ipdb/issues/141

@BjornFJohansson, completions when `ipdb` are obtained from readline in the console, but Spyder doesn't use it because it depends on `qtconsole`, which is a GUI package that imitates how a console works.

Resolving patch:
diff --git a/runtests.py b/runtests.py
--- a/runtests.py
+++ b/runtests.py
@@ -37,6 +37,11 @@ def main(run_slow=False, extra_args=None):
                    '-rw',
                    '--durations=10']
 
+    if sys.version[0] == '3':
+        # Print stack if segmentation fault
+        import faulthandler
+        faulthandler.enable()
+
     if RUN_CI:
         pytest_args += ['-x', '--cov=spyder', '--no-cov-on-fail',
                         '--run-slow']
diff --git a/spyder/plugins/ipythonconsole/widgets/debugging.py b/spyder/plugins/ipythonconsole/widgets/debugging.py
--- a/spyder/plugins/ipythonconsole/widgets/debugging.py
+++ b/spyder/plugins/ipythonconsole/widgets/debugging.py
@@ -345,8 +345,11 @@ def _event_filter_console_keypress(self, event):
             self._tmp_reading = self._reading
             self._reading = False
             try:
-                return super(DebuggingWidget,
-                             self)._event_filter_console_keypress(event)
+                ret = super(DebuggingWidget,
+                            self)._event_filter_console_keypress(event)
+                if key == Qt.Key_Tab:
+                    self.call_kernel(interrupt=True).pong()
+                return ret
             finally:
                 self._reading = self._tmp_reading
         else:
diff --git a/spyder/plugins/ipythonconsole/widgets/namespacebrowser.py b/spyder/plugins/ipythonconsole/widgets/namespacebrowser.py
--- a/spyder/plugins/ipythonconsole/widgets/namespacebrowser.py
+++ b/spyder/plugins/ipythonconsole/widgets/namespacebrowser.py
@@ -63,11 +63,13 @@ def refresh_namespacebrowser(self):
 
     def set_namespace_view(self, view):
         """Set the current namespace view."""
-        self.namespacebrowser.process_remote_view(view)
+        if self.namespacebrowser is not None:
+            self.namespacebrowser.process_remote_view(view)
 
     def set_var_properties(self, properties):
         """Set var properties."""
-        self.namespacebrowser.set_var_properties(properties)
+        if self.namespacebrowser is not None:
+            self.namespacebrowser.set_var_properties(properties)
 
     def set_namespace_view_settings(self):
         """Set the namespace view settings"""


Test patch:
diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
--- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
+++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
@@ -1315,6 +1315,132 @@ def test_console_working_directory(ipyconsole, qtbot):
     assert folders[-1] == NEW_DIR
 
 
+@pytest.mark.slow
+@flaky(max_runs=3)
+@pytest.mark.skipif(not sys.platform.startswith('linux') or PY2,
+                    reason="It only works on Linux with python 3.")
+def test_console_complete(ipyconsole, qtbot):
+    """Test for checking the working directory."""
+    shell = ipyconsole.get_current_shellwidget()
+    qtbot.waitUntil(lambda: shell._prompt_html is not None,
+                    timeout=SHELL_TIMEOUT)
+
+    # Give focus to the widget that's going to receive clicks
+    control = ipyconsole.get_focus_widget()
+    control.setFocus()
+
+    def check_value(name, value):
+        try:
+            return shell.get_value(name) == value
+        except KeyError:
+            return False
+
+    try:
+        # test complete with one result
+        shell.execute('cbs = 1')
+        qtbot.waitUntil(lambda: check_value('cbs', 1))
+        qtbot.wait(500)
+        try:
+            qtbot.keyClicks(control, 'cb')
+            qtbot.keyClick(control, Qt.Key_Tab)
+            # Jedi completion takes time to start up the first time
+            qtbot.wait(5000)
+            qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'cbs')
+        except:
+            # Print shell content before failing
+            print(control.toPlainText())
+            raise
+
+        # test complete with several result
+        shell.execute('cbba = 1')
+        qtbot.waitUntil(lambda: check_value('cbba', 1))
+        qtbot.keyClicks(control, 'cb')
+        qtbot.keyClick(control, Qt.Key_Tab)
+        qtbot.waitUntil(shell._completion_widget.isVisible)
+        # cbs is another solution, so not completed yet
+        assert control.toPlainText().split()[-1] == 'cb'
+        qtbot.keyClick(shell._completion_widget, Qt.Key_Tab)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'cbba')
+
+        shell.execute('abba = 1')
+        qtbot.waitUntil(lambda: check_value('abba', 1))
+
+        # Generate a traceback and enter debugging mode
+        with qtbot.waitSignal(shell.executed):
+            shell.execute('1/0')
+
+        shell.execute('%debug')
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+
+        # Test complete in debug mode
+        # check abba is completed twice (as the cursor moves)
+        qtbot.keyClicks(control, '!ab')
+        qtbot.keyClick(control, Qt.Key_Tab)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == '!abba')
+        qtbot.keyClick(control, Qt.Key_Enter)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+
+        # A second time to check a function call doesn't cause a problem
+        qtbot.keyClicks(control, 'print(ab')
+        qtbot.keyClick(control, Qt.Key_Tab)
+        qtbot.waitUntil(
+            lambda: control.toPlainText().split()[-1] == 'print(abba')
+        qtbot.keyClicks(control, ')')
+        qtbot.keyClick(control, Qt.Key_Enter)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+
+        # Enter an expression
+        qtbot.keyClicks(control, 'baab = 10')
+        qtbot.keyClick(control, Qt.Key_Enter)
+        qtbot.wait(100)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+        qtbot.waitUntil(lambda: check_value('baab', 10))
+
+        # Check baab is completed
+        qtbot.keyClicks(control, 'ba')
+        qtbot.keyClick(control, Qt.Key_Tab)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'baab')
+        qtbot.keyClick(control, Qt.Key_Enter)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+
+        # Get a second ba*
+        qtbot.keyClicks(control, 'ba2ab = 10')
+        qtbot.keyClick(control, Qt.Key_Enter)
+        qtbot.wait(100)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+        qtbot.waitUntil(lambda: check_value('ba2ab', 10))
+
+        # Check the completion widget is shown
+        qtbot.keyClicks(control, 'ba')
+        qtbot.keyClick(control, Qt.Key_Tab)
+        qtbot.waitUntil(shell._completion_widget.isVisible)
+        assert control.toPlainText().split()[-1] == 'ba'
+        qtbot.keyClick(shell._completion_widget, Qt.Key_Tab)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'baab')
+        qtbot.keyClick(control, Qt.Key_Enter)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+
+        # Create a class
+        qtbot.keyClicks(control, '!class A(): baba = 1')
+        qtbot.keyClick(control, Qt.Key_Enter)
+        qtbot.wait(100)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+        qtbot.waitUntil(lambda: shell.is_defined('A'))
+        qtbot.keyClicks(control, '!a = A()')
+        qtbot.keyClick(control, Qt.Key_Enter)
+        qtbot.wait(100)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
+        qtbot.waitUntil(lambda: shell.is_defined('a'))
+
+        # Check we can complete attributes
+        qtbot.keyClicks(control, '!a.ba')
+        qtbot.keyClick(control, Qt.Key_Tab)
+        qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == '!a.baba')
+    except Exception:
+        print(repr(control.toPlainText()))
+        raise
+
+
 @pytest.mark.slow
 @pytest.mark.use_startup_wdir
 def test_pdb_multiline(ipyconsole, qtbot):