Repository: spyder-ide/spyder

Issue:
TypeError in Kite client
## Description

### What steps will reproduce the problem?

<!--- You can use Markdown here --->

When I open up Spyder, after a while (a few seconds), the Issue reporter window pops up. It seems that there's something wrong with kite autocompletion

### Traceback
```python-traceback
  File "C:\Users\Amin\Anaconda3\envs\pmeal\lib\site-packages\spyder\plugins\completion\kite\client.py", line 135, in perform_request
TypeError: 'NoneType' object is not subscriptable
```

## Versions

* Spyder version: 4.0.0b4 
* Python version: 3.7.2
* Qt version: 5.9.7
* PyQt5 version: 5.9.2
* Operating System: Windows 10

### Dependencies

```
pygments >=2.0    :  2.4.2 (OK)
qdarkstyle >=2.6.4:  2.7 (OK)
sphinx >=0.6.6    :  2.1.2 (OK)
pyls >=0.27.0     :  0.28.0 (OK)
nbconvert >=4.0   :  5.6.0 (OK)
pandas >=0.13.1   :  0.25.0 (OK)
numpy >=1.7       :  1.15.0 (OK)
sympy >=0.7.3     :  1.4 (OK)
cython >=0.21     :  None (NOK)
qtconsole >=4.5.2 :  4.5.2 (OK)
IPython >=4.0     :  7.2.0 (OK)
matplotlib >=2.0.0:  None (NOK)
pylint >=0.25     :  2.3.1 (OK)
```



Discussion:
@ma-sadeghi, thanks for reporting. Do you have Kite installed in your system?

@andfoy, please take a look at this one.
@ccordoba12 yes, I have Kite installed.
We're not preventing None responses to not be attended. I'll fix this

Resolving patch:
diff --git a/spyder/plugins/completion/kite/client.py b/spyder/plugins/completion/kite/client.py
--- a/spyder/plugins/completion/kite/client.py
+++ b/spyder/plugins/completion/kite/client.py
@@ -9,16 +9,15 @@
 # Standard library imports
 import logging
 
-# Qt imports
+# Third party imports
 from qtpy.QtCore import QObject, QThread, Signal, QTimer, QMutex
+import requests
 
 # Local imports
 from spyder.plugins.completion.kite import KITE_ENDPOINTS, KITE_REQUEST_MAPPING
 from spyder.plugins.completion.kite.decorators import class_register
 from spyder.plugins.completion.kite.providers import KiteMethodProviderMixIn
-
-# Other imports
-import requests
+from spyder.py3compat import ConnectionError, ConnectionRefusedError
 
 
 logger = logging.getLogger(__name__)
diff --git a/spyder/plugins/completion/kite/providers/document.py b/spyder/plugins/completion/kite/providers/document.py
--- a/spyder/plugins/completion/kite/providers/document.py
+++ b/spyder/plugins/completion/kite/providers/document.py
@@ -82,18 +82,19 @@ def request_document_completions(self, params):
     @handles(LSPRequestTypes.DOCUMENT_COMPLETION)
     def convert_completion_request(self, response):
         spyder_completions = []
-        completions = response['completions']
-        if completions is not None:
-            for completion in completions:
-                entry = {
-                    'kind': KITE_DOCUMENT_TYPES.get(
-                        completion['hint'], CompletionItemKind.TEXT),
-                    'insertText': completion['snippet']['text'],
-                    'filterText': completion['display'],
-                    'sortText': completion['display'][0],
-                    'documentation': completion['documentation']['text']
-                }
-                spyder_completions.append(entry)
+        if response is not None:
+            completions = response['completions']
+            if completions is not None:
+                for completion in completions:
+                    entry = {
+                        'kind': KITE_DOCUMENT_TYPES.get(
+                            completion['hint'], CompletionItemKind.TEXT),
+                        'insertText': completion['snippet']['text'],
+                        'filterText': completion['display'],
+                        'sortText': completion['display'][0],
+                        'documentation': completion['documentation']['text']
+                    }
+                    spyder_completions.append(entry)
         return {'params': spyder_completions}
 
     @send_request(method=LSPRequestTypes.DOCUMENT_HOVER)
@@ -115,11 +116,15 @@ def request_hover(self, params):
 
     @handles(LSPRequestTypes.DOCUMENT_HOVER)
     def process_hover(self, response):
-        # logger.debug(response)
-        report = response['report']
-        text = report['description_text']
-        if len(text) == 0:
+        logger.debug(response)
+        if response is not None:
+            report = response['report']
+            text = report['description_text']
+            if len(text) == 0:
+                text = None
+        else:
             text = None
+
         return {'params': text}
 
     @send_request(method=LSPRequestTypes.DOCUMENT_SIGNATURE)
@@ -137,38 +142,39 @@ def request_signature(self, request):
     @handles(LSPRequestTypes.DOCUMENT_SIGNATURE)
     def process_signature(self, response):
         params = None
-        calls = response['calls']
-        if len(calls) > 0:
-            call = calls[0]
-            callee = call['callee']
-            documentation = callee['synopsis']
-            call_label = callee['repr']
-            signatures = call['signatures']
-            arg_idx = call['arg_index']
-
-            signature = signatures[0]
-            parameters = []
-            names = []
-            logger.debug(signature)
-            for arg in signature['args']:
-                parameters.append({
-                    'label': arg['name'],
-                    'documentation': ''
-                })
-                names.append(arg['name'])
-
-            func_args = ', '.join(names)
-            call_label = '{0}({1})'.format(call_label, func_args)
-
-            base_signature = {
-                'label': call_label,
-                'documentation': documentation,
-                'parameters': parameters
-            }
-            # doc_signatures.append(base_signature)
-            params = {
-                'signatures': base_signature,
-                'activeSignature': 0,
-                'activeParameter': arg_idx
-            }
+        if response is not None:
+            calls = response['calls']
+            if len(calls) > 0:
+                call = calls[0]
+                callee = call['callee']
+                documentation = callee['synopsis']
+                call_label = callee['repr']
+                signatures = call['signatures']
+                arg_idx = call['arg_index']
+
+                signature = signatures[0]
+                parameters = []
+                names = []
+                logger.debug(signature)
+                for arg in signature['args']:
+                    parameters.append({
+                        'label': arg['name'],
+                        'documentation': ''
+                    })
+                    names.append(arg['name'])
+
+                func_args = ', '.join(names)
+                call_label = '{0}({1})'.format(call_label, func_args)
+
+                base_signature = {
+                    'label': call_label,
+                    'documentation': documentation,
+                    'parameters': parameters
+                }
+                # doc_signatures.append(base_signature)
+                params = {
+                    'signatures': base_signature,
+                    'activeSignature': 0,
+                    'activeParameter': arg_idx
+                }
         return {'params': params}
diff --git a/spyder/py3compat.py b/spyder/py3compat.py
--- a/spyder/py3compat.py
+++ b/spyder/py3compat.py
@@ -300,9 +300,16 @@ def iterlists(d, **kw):
 
     viewitems = operator.methodcaller("viewitems")
 
+
+# ============================================================================
+# Exceptions
+# ============================================================================
 if PY2:
+    ConnectionRefusedError = ConnectionError = OSError
     TimeoutError = RuntimeError
 else:
+    ConnectionError = ConnectionError
+    ConnectionRefusedError = ConnectionRefusedError
     TimeoutError = TimeoutError
 
 


Test patch: