Repository: spyder-ide/spyder

Issue:
Rope not autocompleting when *args or **kwargs are present in function definition
### Summary

~~Jedi~~ Rope autocompletion does not work when `*args` or `**kwargs` are present in a function definition.
### Steps to Reproduce
1. Open up a new python file.
2. Type the following:
   
   ``` python
   def function(*args):
       my_variable = 5
       my_v
   #      ^ Press `tab` here
   ```
3. After typing `my_v`, press <kbd>Tab</kbd>. Notice that no autocompletion happens
4. Replace `*args` with `**dict_of_args`.
5. Press <kbd>Tab</kbd> in the same location as before. Again, autocompletion does not work.
6. Remove `**dict_of_args` and repeat. Autocompletion now works.
### Expected Output

Autocompletion of variable names should work even if `*args` or `**kwargs` are present in function definition.
### Additional Info

~~Related [Jedi Issue #558](https://github.com/davidhalter/jedi/issues/588)~~
I thought that I was using Jedi when looking into this issue, but I was mistaken.
### Version and main components
- Spyder Version:  2.3.4
- Python Version:  3.4.3
- Qt Version    :  4.8.6, PyQt4 (API v2) 4.11.3 on Windows
## Optional dependencies

pyflakes >=0.6.0:  0.8.1 (OK)
pep8 >=0.6      :  1.6.2 (OK)
IPython >=1.0   :  3.0.0 (OK)
zmq >=2.1.11    :  14.5.0 (OK)
pygments >=1.6  :  2.0.2 (OK)
pandas >=0.13.1 :  0.16.0 (OK)
sphinx >=0.6.6  :  1.3.1 (OK)
rope >=0.9.2    :  0.9.4-1 (OK)
jedi >=0.8.1    :  0.9.0 (OK)
matplotlib >=1.0:  1.4.3 (OK)
sympy >=0.7.3   :  0.7.6 (OK)
coverage >=0.25 :  3.7.1 (OK)
pylint >=0.25   :  1.4.3 (OK)



Discussion:
Opps, I thought that you guys were using Jedi as the default. According to #2206, Spyder was switched back to Rope (at least for now).

So this issue is actually with **rope_py3k**. I've edited the OP to reflect this.

We use rope if available and fall back on Jedi, so that means neither one is working in this case.  

I checked Jedi via direct calls and it seems to be OK.

``` Python
>>> import jedi
>>> src = """def function(*args):
...     my_variable = 5
...     my_"""
>>> script = jedi.Script(src, 3, 6, '')
>>> script.completions()
[<Completion: my_variable>]                                 # Correct
```

(you can see a bit more info in the Jedi Issue that i mistakenly submitted).

I've tried doing the same thing with `rope-py3k v0.9.4.post1` but I've so far been unable to figure out the API - too much extra stuff for projects and refactoring.

Oh well not that I take a second look, I was able to replicate the problem with direct Rope calls.

Without any argument unpacking (no _args or *_kwargs), it works just fine:

``` Python
>>> import rope.base.project; from rope.contrib import codeassist
>>> src = """def function(kwargs):
...     my_variable = 5
...     my"""
>>> proj = rope.base.project.Project("C:\\Temp")
>>> proposals = codeassist.code_assist(proj, src, 48)
>>> print(proposals)
[my_variable (local, instance)]              # Correct
```

However, once we add unpacking, it goes kaboom:

``` Python
>>> import rope.base.project; from rope.contrib import codeassist
>>> src = """def function(*kwargs):
...     my_variable = 5
...     my"""
>>> proj = rope.base.project.Project("C:\\Temp")
>>> proposals = codeassist.code_assist(proj, src, 48)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\WinPython34_x64\python-3.4.3.amd64\lib\site-packages\rope\contrib\codeassist.py", line 33, in code_assist
    return assist()
  File "C:\WinPython34_x64\python-3.4.3.amd64\lib\site-packages\rope\contrib\codeassist.py", line 325, in __call__
    completions = list(self._code_completions().values())
  File "C:\WinPython34_x64\python-3.4.3.amd64\lib\site-packages\rope\contrib\codeassist.py", line 408, in _code_completions
    self._undotted_completions(inner_scope, result, lineno=lineno)
  File "C:\WinPython34_x64\python-3.4.3.amd64\lib\site-packages\rope\contrib\codeassist.py", line 353, in _undotted_completions
    if name.startswith(self.starting):
AttributeError: 'arg' object has no attribute 'startswith'
```

Sadly it doesn't look like there's any more active development on the [rope_py3k project](https://bitbucket.org/zjes/rope_py3k/overview) over on BitBucket.

I tested this on 2.3.4 and was able to reproduce.  However, it is already fixed in the current master.  @dougthor42, you can install the latest  `pip install git+https://github.com/spyder-ide/spyder`, or wait for the next official release.

I cloned the master branch just now (~11:45am PDT 2015-05-20) and bootstrapped into Spyder and I still get the same issue.

I tried direct function calls again and Rope still fails, while Jedi still works. So to me it _seems_ like the fallback on Jedi is not working, but I may not completely understand the fallback logic.

So I decided to hack it into Spyder myself. I've edited `get_completions` in `rope_plugin.RopePlugin` so that it uses Jedi if Rope raises `AttributeError`.

Here's the new code:

``` Python
    def get_completions(self, info):
            ...
            return [(proposal.name, proposal.type) for proposal in proposals]
        except AttributeError as _error:
            # fall back on Jedi
            try:
                import jedi
                # need to convert offset to line and col nums
                lines = source_code[:offset].splitlines()
                line = len(lines)
                column = len(lines[-1])
                script = jedi.Script(source_code,
                                     line,
                                     column,
                                     source_encoding='utf-8')
                completions = script.completions()
                return [(c.name, c.type) for c in completions]
            except Exception as _error:      #analysis:ignore
                if DEBUG_EDITOR:
                    log_last_error(LOG_FILENAME,
                                   "Rope (with Jedi Fallback) get_completion_list")
        except Exception as _error:  #analysis:ignore
        ...
```

I want to attach a diff but github only allows images, so check it out [here](https://www.dropbox.com/s/mmoix63hk65ww1x/rope_plugin_jedi_fallback.diff?dl=0).

I don't think that this change warrants a pull/merge request, especially since you guys think that it's fixed and because I doubt my code is production worthy :-P.

If you use it, great. If you fix it another way, that's cool too.

@dougthor42, can you try running as `python bootstrap.py --debug`, and paste the the console output when you attempt the completion?

Here ya go, with some light formatting to make it a bit easier to read.

```
C:\WinPython34_temp\spyder-ide>python bootstrap.py --debug
Executing Spyder from source checkout
Revision None:None, Branch: None
01. Patched sys.path with C:\WinPython34_temp\spyder-ide
                      and C:\WinPython34_temp\spyder-ide\external-py3
02. No PyQt5 or PySide detected, using PyQt4 if available
0x. Switching debug mode on
03. Imported Spyder 3.0.0dev
    [Python 3.4.3 64bits, Qt 4.8.6, PyQt4 (API v2) 4.11.3 on Windows]
0x. Enforcing parent console (Windows only)
04. Executing spyder.main()
Bootstrap completed in 00:00:00.9021
Start of MainWindow constructor
appusermodelid: 0
End of MainWindow constructor
*** Start of MainWindow setup ***
  ..core actions
  ..toolbars
  ..tools
  ..sift?
  ..plugin: internal console
    ..internal console: initializing
  ..plugin: working directory
Loading object inspector...
Loading outline explorer...
Loading editor...
Instropection Plugin Loaded: rope
Instropection Plugin Loaded: jedi
Instropection Plugin Loaded: fallback
Plugins loaded: KeysView(OrderedDict([
    ('rope', <spyderlib.utils.introspection.rope_plugin.RopePlugin object at 0x00000000092312B0>),
    ('jedi', <spyderlib.utils.introspection.jedi_plugin.JediPlugin object at 0x0000000009231BE0>),
    ('fallback', <spyderlib.utils.introspection.fallback_plugin.FallbackPlugin object at 0x000000000A9734A8>)
    ]))
  ..widgets
Loading file explorer...
Loading history plugin...
Loading online help...
Loading project explorer...
Loading external console...
Loading namespace browser...
Loading IPython console...
Setting up main window...
Setting up window...
*** End of MainWindow setup ***
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40) [MSC v.1600 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> completions request
'Completion' object has no attribute 'word'
No valid responses acquired
got timeout: [<spyderlib.utils.introspection.rope_plugin.RopePlugin object at 0x00000000092312B0>,
    <spyderlib.utils.introspection.jedi_plugin.JediPlugin object at 0x0000000009231BE0>
    ]
completions request from jedi finished: "None" in 0.2 sec
```

This was run using the same example code as I posted in the original report.

Edit:

Here's what it looks like when I run it without any unpacking (no `*args` or `**kwargs`):

```
...
completions request
'Completion' object has no attribute 'word'
completions request from rope finished: "[('my_variable', 'instance')]" in 0.0 sec
got timeout: [<spyderlib.utils.introspection.rope_plugin.RopePlugin object at 0x00000000092312B0>,
    <spyderlib.utils.introspection.jedi_plugin.JediPlugin object at 0x0000000009231BE0>
    ]
```

Shucks, the Jedi API changed from 0.8.1 -> 0.9.0, I'll have to paper around it.

Always tricky when APIs change.

Thanks for looking into it.


Resolving patch:
diff --git a/spyderlib/utils/introspection/jedi_plugin.py b/spyderlib/utils/introspection/jedi_plugin.py
--- a/spyderlib/utils/introspection/jedi_plugin.py
+++ b/spyderlib/utils/introspection/jedi_plugin.py
@@ -56,7 +56,7 @@ def load_plugin(self):
     def get_completions(self, info):
         """Return a list of (completion, type) tuples"""
         completions = self.get_jedi_object('completions', info)
-        completions = [(c.word, c.type) for c in completions]
+        completions = [(c.name, c.type) for c in completions]
         debug_print(str(completions)[:100])
         return completions
 


Test patch: