JulianEberius / SublimeRope

ST2 only, use SublimePythonIDE with ST3: Adds Python completions and some IDE-like functions to Sublime Text 2, through the use of the Rope library
GNU General Public License v2.0
250 stars 26 forks source link

frequent traceback on autocomplete #6

Closed glyph closed 12 years ago

glyph commented 12 years ago

I'm not sure what the difference is between when I see this and when autocompletion just works, but I frequently see this in my log:

Traceback (most recent call last): File "./sublime_plugin.py", line 175, in on_query_completions File "./python_completions.py", line 64, in on_query_completions context.project, context.input, loc, context.resource) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/contrib/codeassist.py", line 33, in code_assist return assist() File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/contrib/codeassist.py", line 325, in call completions = list(self._code_completions().values()) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/contrib/codeassist.py", line 408, in _code_completions self._undotted_completions(inner_scope, result, lineno=lineno) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/contrib/codeassist.py", line 351, in _undotted_completions names = scope.get_names() File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyscopes.py", line 16, in get_names return self.pyobject.get_attributes() File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyobjects.py", line 196, in get_attributes result = dict(self._get_concluded_attributes()) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/utils.py", line 25, in newfunc return func(self, _args, _kwds) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyobjects.py", line 191, in _get_concluded_attributes self.concluded_attributes.set(self._create_concluded_attributes()) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyobjectsdef.py", line 127, in _create_concluded_attributes for base in reversed(self.get_superclasses()): File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyobjectsdef.py", line 119, in get_superclasses self._superclasses.set(self._get_bases()) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyobjectsdef.py", line 135, in _get_bases base_name) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/evaluate.py", line 25, in eval_node return eval_node2(scope, node)[1] File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/evaluate.py", line 30, in eval_node2 ast.walk(node, evaluator) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/ast.py", line 34, in walk return method(node) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/evaluate.py", line 150, in _Attribute if pyname.get_object() != rope.base.pyobjects.get_unknown(): File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/utils.py", line 25, in newfunc return func(self, _args, _kwds) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pynames.py", line 158, in get_object return self._get_imported_pyname().get_object() File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pynames.py", line 149, in _get_imported_pyname result = self.imported_module.get_object()[self.imported_name] File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyobjects.py", line 28, in getitem return self.get_attribute(key) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyobjects.py", line 202, in get_attribute if name in self._get_structural_attributes(): File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/utils.py", line 25, in newfunc return func(self, _args, _kwds) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyobjects.py", line 184, in _get_structural_attributes self.structural_attributes = self._create_structural_attributes() File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pyobjectsdef.py", line 225, in _create_structural_attributes extension_submodules = self.pycore._builtin_submodules(modname) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pycore.py", line 88, in _builtin_submodules for extension in self.extension_modules: File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/utils.py", line 10, in _wrapper setattr(self, name, func(self, _args, _kwds)) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/pycore.py", line 294, in extension_modules result.update(stdmods.dynload_modules()) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/utils.py", line 74, in call result = self.func(_args, *_kwds) File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/stdmods.py", line 33, in dynload_modules dynload_path = os.path.join(_stdlib_path(), 'lib-dynload') File "/home/glyph/.config/sublime-text-2/Packages/SublimeRope/rope/base/stdmods.py", line 8, in _stdlib_path import distutils.sysconfig ImportError: No module named distutils.sysconfig

OscarL commented 12 years ago

The rope library included in SublimeRope is trying to figure out the path to the installed Python standard lib, and since Julian updated rope (changeset: 92fcb6e9c1719327128454a636732afd6bd3f7e9) it does this assuming "distutils" is available. That is not always the case (certainly not a stdlib package), and it is not included in SublimeText's Python library (python26.zip).

The specific change was (on rope/base/stdmods.py):

def _stdlib_path():
-    import inspect
-    return os.path.dirname(inspect.getsourcefile(inspect))
+    import distutils.sysconfig
+    return distutils.sysconfig.get_python_lib(standard_lib=True)

The old code always returned 'python26.zip', that is, the Python stdlib, but the one included in SublimeText, not the one for the system-wide Python installation.

OscarL commented 12 years ago

As a possible solution, I would change that function into this:

def _stdlib_path():
    try:
        # Try to get Python's libs path from the systems's default python interpreter.
        import subprocess
        args = ['python', '-c', 'import os; print os.path.dirname(os.__file__)']
        return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0].splitlines()[0]
    except OSError:
        # Get the Python's libs path for the interpreter executing this function.
        import os
        return os.path.dirname(os.__file__)
glyph commented 12 years ago

Actually, distutils is a standard library package. See http://docs.python.org/library/distutils.html for example - notice the breadcrumbs:

Python v2.7.2 documentation » The Python Standard Library » 27. Python Runtime Services »
OscarL commented 12 years ago

Silly me. Your are, of course, correct.

The problem you are seeing has, still, the same cause: rope assuming distutils is available on the interpreter that is running it. And as SublimeText2 Python's libs do not include it... we end up having those tracebacks.

So, we patch rope, or ST2 starts to include distutils :-).

JulianEberius commented 12 years ago

Hi,

Sorry I didn't have a look at SublimeRope in a long time so I saw your discussion just now. I do not know what the simplest way of finding the standard lib path is without using distutils... os.path.dirname(os.file) seems very simple and nice, and I would patch the bundled Rope to use it, but I don't know if this will work on all platforms.

OscarL commented 12 years ago

Ok. Just for the record, it works on Windows XP (Python 2.5 and 2.7), and I've just tested it on Ubuntu 10.10 (Python 2.6 and 2.7). There, it returns "/usr/lib/python2.6" and "/usr/lib/python2.7", which seems correct to me. I have no access to any OS X system, so that remains untested.

edit: also works on Linux Mint 11 (python 2.7).

JulianEberius commented 12 years ago

I have decided for the common solution of using "try... except ImportError" The old code is executed when distutils can not be imported. This should (in theory) work for everybody.

Greetings, Julian

OscarL commented 12 years ago

Looks good to me.

It remains to be seen if there is actually any noticeable difference in returning the lib's path for the interpreter running rope (ST2's py2.6), and the one for the interpreter that will run the code that rope is analyzing (who knows what python version?, that's why I've proposed the "python -c" solution).

On my quick tests, I haven't found anything unusual, at least with the rope's functionality actually used by SublimeRope.

glyph commented 12 years ago

I like the python -c solution too; ST2's py2.6 may not include source code, and may not include the right version of the source anyway (I'm developing against py2.7).

JulianEberius commented 12 years ago

I think choosing the correct Python should be done through a virtualenv. Select "Rope: New Project" in the Command Palette. Then you can specify the project dir and the virtualenv dir. Even if you do not use virtualenv, I think you should be able to specify your Python's root directory as virtualenv directory. For example, running OSX and installing Python2.7 with Homebrew will put Python in "/usr/local/Cellar/python/2.7.2". If I give this to "Rope: New Project" it picks up the correct standard lib.

I do no like running a new process (python -c starts a new process) for every completion. But of course it could be done if necessary.