forked from VimPlug/jedi
Merge branch 'dev' into tokenizer
This commit is contained in:
+18
@@ -0,0 +1,18 @@
|
|||||||
|
[run]
|
||||||
|
omit =
|
||||||
|
jedi/_compatibility.py
|
||||||
|
|
||||||
|
[report]
|
||||||
|
# Regexes for lines to exclude from consideration
|
||||||
|
exclude_lines =
|
||||||
|
# Don't complain about missing debug-only code:
|
||||||
|
def __repr__
|
||||||
|
if self\.debug
|
||||||
|
|
||||||
|
# Don't complain if tests don't hit defensive assertion code:
|
||||||
|
raise AssertionError
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# Don't complain if non-runnable code isn't run:
|
||||||
|
if 0:
|
||||||
|
if __name__ == .__main__.:
|
||||||
+3
-1
@@ -1,9 +1,11 @@
|
|||||||
*~
|
*~
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
|
*.pyc
|
||||||
.ropeproject
|
.ropeproject
|
||||||
.tox
|
.tox
|
||||||
*.pyc
|
.coveralls.yml
|
||||||
|
.coverage
|
||||||
/build/
|
/build/
|
||||||
/docs/_build/
|
/docs/_build/
|
||||||
/dist/
|
/dist/
|
||||||
|
|||||||
+10
@@ -4,7 +4,17 @@ env:
|
|||||||
- TOXENV=py26
|
- TOXENV=py26
|
||||||
- TOXENV=py27
|
- TOXENV=py27
|
||||||
- TOXENV=py32
|
- TOXENV=py32
|
||||||
|
- TOXENV=py33
|
||||||
|
- TOXENV=cov
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- env: TOXENV=cov
|
||||||
install:
|
install:
|
||||||
- pip install --quiet --use-mirrors tox
|
- pip install --quiet --use-mirrors tox
|
||||||
script:
|
script:
|
||||||
- tox
|
- tox
|
||||||
|
after_script:
|
||||||
|
- if [ $TOXENV == "cov" ]; then
|
||||||
|
pip install --quiet --use-mirrors coveralls;
|
||||||
|
coveralls;
|
||||||
|
fi
|
||||||
|
|||||||
+27
@@ -6,6 +6,11 @@ Jedi - an awesome autocompletion library for Python
|
|||||||
:target: http://travis-ci.org/davidhalter/jedi
|
:target: http://travis-ci.org/davidhalter/jedi
|
||||||
:alt: Travis-CI build status
|
:alt: Travis-CI build status
|
||||||
|
|
||||||
|
.. image:: https://coveralls.io/repos/davidhalter/jedi/badge.png?branch=master
|
||||||
|
:target: https://coveralls.io/r/davidhalter/jedi
|
||||||
|
:alt: Coverage Status
|
||||||
|
|
||||||
|
|
||||||
Jedi is an autocompletion tool for Python that can be used in IDEs/editors.
|
Jedi is an autocompletion tool for Python that can be used in IDEs/editors.
|
||||||
Jedi works. Jedi is fast. It understands all of the basic Python syntax
|
Jedi works. Jedi is fast. It understands all of the basic Python syntax
|
||||||
elements including many builtin functions.
|
elements including many builtin functions.
|
||||||
@@ -86,3 +91,25 @@ API for IDEs
|
|||||||
It's very easy to create an editor plugin that uses Jedi. See
|
It's very easy to create an editor plugin that uses Jedi. See
|
||||||
https://jedi.readthedocs.org/en/latest/docs/plugin-api.html for more
|
https://jedi.readthedocs.org/en/latest/docs/plugin-api.html for more
|
||||||
information.
|
information.
|
||||||
|
|
||||||
|
|
||||||
|
Testing
|
||||||
|
=======
|
||||||
|
|
||||||
|
The test suite depends on ``tox`` and ``pytest``::
|
||||||
|
|
||||||
|
pip install tox pytest
|
||||||
|
|
||||||
|
To run the tests for all supported Python versions::
|
||||||
|
|
||||||
|
PIP_INSECURE=t tox
|
||||||
|
|
||||||
|
If you want to test only a specific Python version (e.g. Python 2.7), it's as
|
||||||
|
easy as ::
|
||||||
|
|
||||||
|
tox -e py27
|
||||||
|
|
||||||
|
The ``PIP_INSECURE=t`` env variable is only needed for the ``py25`` target.
|
||||||
|
|
||||||
|
Tests are also run automatically on `Travis CI
|
||||||
|
<https://travis-ci.org/davidhalter/jedi/>`_.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ A little history
|
|||||||
|
|
||||||
The Star Wars Jedi are awesome. My Jedi software tries to imitate a little bit
|
The Star Wars Jedi are awesome. My Jedi software tries to imitate a little bit
|
||||||
of the precognition the Jedi have. There's even an awesome `scene
|
of the precognition the Jedi have. There's even an awesome `scene
|
||||||
<http://www.youtube.com/watch?v=5BDO3pyavOY>`_ of Monty Python Jedi's :-).
|
<http://www.youtube.com/watch?v=5BDO3pyavOY>`_ of Monty Python Jedis :-).
|
||||||
|
|
||||||
But actually the name hasn't so much to do with Star Wars. It's part of my
|
But actually the name hasn't so much to do with Star Wars. It's part of my
|
||||||
second name.
|
second name.
|
||||||
@@ -13,13 +13,13 @@ second name.
|
|||||||
After I explained Guido van Rossum, how some parts of my auto-completion work,
|
After I explained Guido van Rossum, how some parts of my auto-completion work,
|
||||||
he said (we drank a beer or two):
|
he said (we drank a beer or two):
|
||||||
|
|
||||||
*Oh, that worries me*
|
*"Oh, that worries me..."*
|
||||||
|
|
||||||
When it's finished, I hope he'll like it :-)
|
When it's finished, I hope he'll like it :-)
|
||||||
|
|
||||||
I actually started Jedi, because there were no good solutions available for
|
I actually started Jedi, because there were no good solutions available for VIM.
|
||||||
VIM. Most auto-completions just didn't work well. The only good solution was
|
Most auto-completions just didn't work well. The only good solution was PyCharm.
|
||||||
PyCharm. I just like my good old VIM. Rope was never really intended to be an
|
But I like my good old VIM. Rope was never really intended to be an
|
||||||
auto-completion (and also I really hate project folders for my Python scripts).
|
auto-completion (and also I really hate project folders for my Python scripts).
|
||||||
It's more of a refactoring suite. So I decided to do my own version of a
|
It's more of a refactoring suite. So I decided to do my own version of a
|
||||||
completion, which would execute non-dangerous code. But I soon realized, that
|
completion, which would execute non-dangerous code. But I soon realized, that
|
||||||
|
|||||||
+59
-1
@@ -7,11 +7,69 @@ Most of the code here is necessary to support Python 2.5. Once this dependency
|
|||||||
will be dropped, we'll get rid of most code.
|
will be dropped, we'll get rid of most code.
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
import imp
|
||||||
|
import os
|
||||||
|
try:
|
||||||
|
import importlib
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
is_py3k = sys.hexversion >= 0x03000000
|
is_py3k = sys.hexversion >= 0x03000000
|
||||||
|
is_py33 = sys.hexversion >= 0x03030000
|
||||||
is_py25 = sys.hexversion < 0x02060000
|
is_py25 = sys.hexversion < 0x02060000
|
||||||
|
|
||||||
|
def find_module_py33(string, path=None):
|
||||||
|
mod_info = (None, None, None)
|
||||||
|
loader = None
|
||||||
|
if path is not None:
|
||||||
|
# Check for the module in the specidied path
|
||||||
|
loader = importlib.machinery.PathFinder.find_module(string, path)
|
||||||
|
else:
|
||||||
|
# Check for the module in sys.path
|
||||||
|
loader = importlib.machinery.PathFinder.find_module(string, sys.path)
|
||||||
|
if loader is None:
|
||||||
|
# Fallback to find builtins
|
||||||
|
loader = importlib.find_loader(string)
|
||||||
|
|
||||||
|
if loader is None:
|
||||||
|
raise ImportError
|
||||||
|
|
||||||
|
try:
|
||||||
|
if (loader.is_package(string)):
|
||||||
|
mod_info = (None, os.path.dirname(loader.path), True)
|
||||||
|
else:
|
||||||
|
filename = loader.get_filename(string)
|
||||||
|
if filename and os.path.exists(filename):
|
||||||
|
mod_info = (open(filename, 'U'), filename, False)
|
||||||
|
else:
|
||||||
|
mod_info = (None, filename, False)
|
||||||
|
except AttributeError:
|
||||||
|
mod_info = (None, loader.load_module(string).__name__, False)
|
||||||
|
|
||||||
|
return mod_info
|
||||||
|
|
||||||
|
def find_module_pre_py33(string, path=None):
|
||||||
|
mod_info = None
|
||||||
|
if path is None:
|
||||||
|
mod_info = imp.find_module(string)
|
||||||
|
else:
|
||||||
|
mod_info = imp.find_module(string, path)
|
||||||
|
|
||||||
|
return (mod_info[0], mod_info[1], mod_info[2][2] == imp.PKG_DIRECTORY)
|
||||||
|
|
||||||
|
def find_module(string, path=None):
|
||||||
|
"""Provides information about a module.
|
||||||
|
|
||||||
|
This function isolates the differences in importing libraries introduced with
|
||||||
|
python 3.3 on; it gets a module name and optionally a path. It will return a
|
||||||
|
tuple containin an open file for the module (if not builtin), the filename
|
||||||
|
or the name of the module if it is a builtin one and a boolean indicating
|
||||||
|
if the module is contained in a package."""
|
||||||
|
if is_py33:
|
||||||
|
return find_module_py33(string, path)
|
||||||
|
else:
|
||||||
|
return find_module_pre_py33(string, path)
|
||||||
|
|
||||||
# next was defined in python 2.6, in python 3 obj.next won't be possible
|
# next was defined in python 2.6, in python 3 obj.next won't be possible
|
||||||
# anymore
|
# anymore
|
||||||
try:
|
try:
|
||||||
|
|||||||
+4
-6
@@ -3,6 +3,7 @@ The :mod:`api_classes` module contains the return classes of the API. These
|
|||||||
classes are the much bigger part of the whole API, because they contain the
|
classes are the much bigger part of the whole API, because they contain the
|
||||||
interesting information about completion and goto operations.
|
interesting information about completion and goto operations.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
@@ -11,6 +12,7 @@ import functools
|
|||||||
|
|
||||||
from jedi._compatibility import unicode, next
|
from jedi._compatibility import unicode, next
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
|
from jedi import common
|
||||||
from jedi import parsing_representation as pr
|
from jedi import parsing_representation as pr
|
||||||
from jedi import cache
|
from jedi import cache
|
||||||
import keywords
|
import keywords
|
||||||
@@ -144,10 +146,8 @@ class BaseDefinition(object):
|
|||||||
if not isinstance(self.definition, keywords.Keyword):
|
if not isinstance(self.definition, keywords.Keyword):
|
||||||
par = self.definition
|
par = self.definition
|
||||||
while par is not None:
|
while par is not None:
|
||||||
try:
|
with common.ignored(AttributeError):
|
||||||
path.insert(0, par.name)
|
path.insert(0, par.name)
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
par = par.parent
|
par = par.parent
|
||||||
return path
|
return path
|
||||||
|
|
||||||
@@ -295,10 +295,8 @@ class BaseDefinition(object):
|
|||||||
if not path:
|
if not path:
|
||||||
return None # for keywords the path is empty
|
return None # for keywords the path is empty
|
||||||
|
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
path[0] = self._mapping[path[0]]
|
path[0] = self._mapping[path[0]]
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
for key, repl in self._tuple_mapping.items():
|
for key, repl in self._tuple_mapping.items():
|
||||||
if tuple(path[:len(key)]) == key:
|
if tuple(path[:len(key)]) == key:
|
||||||
path = [repl] + path[len(key):]
|
path = [repl] + path[len(key):]
|
||||||
|
|||||||
+55
-16
@@ -21,10 +21,16 @@ from __future__ import with_statement
|
|||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import pickle
|
import hashlib
|
||||||
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except:
|
||||||
|
import pickle
|
||||||
|
import shutil
|
||||||
|
|
||||||
from jedi._compatibility import json
|
from jedi._compatibility import json
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
|
from jedi import common
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
|
|
||||||
# memoize caches will be deleted after every action
|
# memoize caches will be deleted after every action
|
||||||
@@ -143,12 +149,10 @@ def cache_function_definition(stmt):
|
|||||||
|
|
||||||
def cache_star_import(func):
|
def cache_star_import(func):
|
||||||
def wrapper(scope, *args, **kwargs):
|
def wrapper(scope, *args, **kwargs):
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
mods = star_import_cache[scope]
|
mods = star_import_cache[scope]
|
||||||
if mods[0] + settings.star_import_cache_validity > time.time():
|
if mods[0] + settings.star_import_cache_validity > time.time():
|
||||||
return mods[1]
|
return mods[1]
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
# cache is too old and therefore invalid or not available
|
# cache is too old and therefore invalid or not available
|
||||||
invalidate_star_import_cache(scope)
|
invalidate_star_import_cache(scope)
|
||||||
mods = func(scope, *args, **kwargs)
|
mods = func(scope, *args, **kwargs)
|
||||||
@@ -160,15 +164,13 @@ def cache_star_import(func):
|
|||||||
|
|
||||||
def invalidate_star_import_cache(module, only_main=False):
|
def invalidate_star_import_cache(module, only_main=False):
|
||||||
""" Important if some new modules are being reparsed """
|
""" Important if some new modules are being reparsed """
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
t, mods = star_import_cache[module]
|
t, mods = star_import_cache[module]
|
||||||
|
|
||||||
del star_import_cache[module]
|
del star_import_cache[module]
|
||||||
|
|
||||||
for m in mods:
|
for m in mods:
|
||||||
invalidate_star_import_cache(m, only_main=True)
|
invalidate_star_import_cache(m, only_main=True)
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not only_main:
|
if not only_main:
|
||||||
# We need a list here because otherwise the list is being changed
|
# We need a list here because otherwise the list is being changed
|
||||||
@@ -216,13 +218,36 @@ def save_module(path, name, parser, pickling=True):
|
|||||||
|
|
||||||
|
|
||||||
class _ModulePickling(object):
|
class _ModulePickling(object):
|
||||||
|
|
||||||
|
version = 2
|
||||||
|
"""
|
||||||
|
Version number (integer) for file system cache.
|
||||||
|
|
||||||
|
Increment this number when there are any incompatible changes in
|
||||||
|
parser representation classes. For example, the following changes
|
||||||
|
are regarded as incompatible.
|
||||||
|
|
||||||
|
- Class name is changed.
|
||||||
|
- Class is moved to another module.
|
||||||
|
- Defined slot of the class is changed.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__index = None
|
self.__index = None
|
||||||
self.py_version = '%s.%s' % sys.version_info[:2]
|
self.py_tag = 'cpython-%s%s' % sys.version_info[:2]
|
||||||
|
"""
|
||||||
|
Short name for distinguish Python implementations and versions.
|
||||||
|
|
||||||
|
It's like `sys.implementation.cache_tag` but for Python < 3.3
|
||||||
|
we generate something similar. See:
|
||||||
|
http://docs.python.org/3/library/sys.html#sys.implementation
|
||||||
|
|
||||||
|
.. todo:: Detect interpreter (e.g., PyPy).
|
||||||
|
"""
|
||||||
|
|
||||||
def load_module(self, path, original_changed_time):
|
def load_module(self, path, original_changed_time):
|
||||||
try:
|
try:
|
||||||
pickle_changed_time = self._index[self.py_version][path]
|
pickle_changed_time = self._index[path]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
if original_changed_time is not None \
|
if original_changed_time is not None \
|
||||||
@@ -240,10 +265,10 @@ class _ModulePickling(object):
|
|||||||
def save_module(self, path, parser_cache_item):
|
def save_module(self, path, parser_cache_item):
|
||||||
self.__index = None
|
self.__index = None
|
||||||
try:
|
try:
|
||||||
files = self._index[self.py_version]
|
files = self._index
|
||||||
except KeyError:
|
except KeyError:
|
||||||
files = {}
|
files = {}
|
||||||
self._index[self.py_version] = files
|
self._index = files
|
||||||
|
|
||||||
with open(self._get_hashed_path(path), 'wb') as f:
|
with open(self._get_hashed_path(path), 'wb') as f:
|
||||||
pickle.dump(parser_cache_item, f, pickle.HIGHEST_PROTOCOL)
|
pickle.dump(parser_cache_item, f, pickle.HIGHEST_PROTOCOL)
|
||||||
@@ -256,9 +281,16 @@ class _ModulePickling(object):
|
|||||||
if self.__index is None:
|
if self.__index is None:
|
||||||
try:
|
try:
|
||||||
with open(self._get_path('index.json')) as f:
|
with open(self._get_path('index.json')) as f:
|
||||||
self.__index = json.load(f)
|
data = json.load(f)
|
||||||
except IOError:
|
except IOError:
|
||||||
self.__index = {}
|
self.__index = {}
|
||||||
|
else:
|
||||||
|
# 0 means version is not defined (= always delete cache):
|
||||||
|
if data.get('version', 0) != self.version:
|
||||||
|
self.delete_cache()
|
||||||
|
self.__index = {}
|
||||||
|
else:
|
||||||
|
self.__index = data['index']
|
||||||
return self.__index
|
return self.__index
|
||||||
|
|
||||||
def _remove_old_modules(self):
|
def _remove_old_modules(self):
|
||||||
@@ -269,18 +301,25 @@ class _ModulePickling(object):
|
|||||||
self._index # reload index
|
self._index # reload index
|
||||||
|
|
||||||
def _flush_index(self):
|
def _flush_index(self):
|
||||||
|
data = {'version': self.version, 'index': self._index}
|
||||||
with open(self._get_path('index.json'), 'w') as f:
|
with open(self._get_path('index.json'), 'w') as f:
|
||||||
json.dump(self._index, f)
|
json.dump(data, f)
|
||||||
self.__index = None
|
self.__index = None
|
||||||
|
|
||||||
|
def delete_cache(self):
|
||||||
|
shutil.rmtree(self._cache_directory())
|
||||||
|
|
||||||
def _get_hashed_path(self, path):
|
def _get_hashed_path(self, path):
|
||||||
return self._get_path('%s_%s.pkl' % (self.py_version, hash(path)))
|
return self._get_path('%s.pkl' % hashlib.md5(path.encode("utf-8")).hexdigest())
|
||||||
|
|
||||||
def _get_path(self, file):
|
def _get_path(self, file):
|
||||||
dir = settings.cache_directory
|
dir = self._cache_directory()
|
||||||
if not os.path.exists(dir):
|
if not os.path.exists(dir):
|
||||||
os.makedirs(dir)
|
os.makedirs(dir)
|
||||||
return dir + os.path.sep + file
|
return os.path.join(dir, file)
|
||||||
|
|
||||||
|
def _cache_directory(self):
|
||||||
|
return os.path.join(settings.cache_directory, self.py_tag)
|
||||||
|
|
||||||
|
|
||||||
# is a singleton
|
# is a singleton
|
||||||
|
|||||||
@@ -163,3 +163,13 @@ def indent_block(text, indention=' '):
|
|||||||
text = text[:-1]
|
text = text[:-1]
|
||||||
lines = text.split('\n')
|
lines = text.split('\n')
|
||||||
return '\n'.join(map(lambda s: indention + s, lines)) + temp
|
return '\n'.join(map(lambda s: indention + s, lines)) + temp
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def ignored(*exceptions):
|
||||||
|
"""Context manager that ignores all of the specified exceptions. This will
|
||||||
|
be in the standard library starting with Python 3.4."""
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
except exceptions:
|
||||||
|
pass
|
||||||
|
|||||||
+2
-3
@@ -59,6 +59,7 @@ from jedi import cache
|
|||||||
from jedi import parsing_representation as pr
|
from jedi import parsing_representation as pr
|
||||||
from jedi import modules
|
from jedi import modules
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
|
from jedi import common
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import fast_parser
|
from jedi import fast_parser
|
||||||
import api_classes
|
import api_classes
|
||||||
@@ -487,10 +488,8 @@ def related_name_add_import_modules(definitions, search_name):
|
|||||||
for d in definitions:
|
for d in definitions:
|
||||||
if isinstance(d.parent, pr.Import):
|
if isinstance(d.parent, pr.Import):
|
||||||
s = imports.ImportPath(d.parent, direct_resolve=True)
|
s = imports.ImportPath(d.parent, direct_resolve=True)
|
||||||
try:
|
with common.ignored(IndexError):
|
||||||
new.add(s.follow(is_goto=True)[0])
|
new.add(s.follow(is_goto=True)[0])
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
return set(definitions) | new
|
return set(definitions) | new
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+6
-15
@@ -68,6 +68,7 @@ backtracking algorithm.
|
|||||||
|
|
||||||
.. todo:: nonlocal statement, needed or can be ignored? (py3k)
|
.. todo:: nonlocal statement, needed or can be ignored? (py3k)
|
||||||
"""
|
"""
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import itertools
|
import itertools
|
||||||
@@ -429,11 +430,9 @@ def find_name(scope, name_str, position=None, search_global=False,
|
|||||||
if isinstance(scope, (er.Instance, er.Class)) \
|
if isinstance(scope, (er.Instance, er.Class)) \
|
||||||
and hasattr(r, 'get_descriptor_return'):
|
and hasattr(r, 'get_descriptor_return'):
|
||||||
# handle descriptors
|
# handle descriptors
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
res_new += r.get_descriptor_return(scope)
|
res_new += r.get_descriptor_return(scope)
|
||||||
continue
|
continue
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
res_new.append(r)
|
res_new.append(r)
|
||||||
return res_new
|
return res_new
|
||||||
|
|
||||||
@@ -462,19 +461,15 @@ def check_getattr(inst, name_str):
|
|||||||
# str is important to lose the NamePart!
|
# str is important to lose the NamePart!
|
||||||
module = builtin.Builtin.scope
|
module = builtin.Builtin.scope
|
||||||
name = pr.Call(module, str(name_str), pr.Call.STRING, (0, 0), inst)
|
name = pr.Call(module, str(name_str), pr.Call.STRING, (0, 0), inst)
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
result = inst.execute_subscope_by_name('__getattr__', [name])
|
result = inst.execute_subscope_by_name('__getattr__', [name])
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
if not result:
|
if not result:
|
||||||
# this is a little bit special. `__getattribute__` is executed
|
# this is a little bit special. `__getattribute__` is executed
|
||||||
# before anything else. But: I know no use case, where this
|
# before anything else. But: I know no use case, where this
|
||||||
# could be practical and the jedi would return wrong types. If
|
# could be practical and the jedi would return wrong types. If
|
||||||
# you ever have something, let me know!
|
# you ever have something, let me know!
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
result = inst.execute_subscope_by_name('__getattribute__', [name])
|
result = inst.execute_subscope_by_name('__getattribute__', [name])
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -536,10 +531,8 @@ def assign_tuples(tup, results, seek_name):
|
|||||||
debug.warning("invalid tuple lookup %s of result %s in %s"
|
debug.warning("invalid tuple lookup %s of result %s in %s"
|
||||||
% (tup, results, seek_name))
|
% (tup, results, seek_name))
|
||||||
else:
|
else:
|
||||||
try:
|
with common.ignored(IndexError):
|
||||||
types += func(index)
|
types += func(index)
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
return types
|
return types
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
@@ -648,11 +641,9 @@ def follow_call_list(call_list, follow_array=False):
|
|||||||
call = next(calls_iterator)
|
call = next(calls_iterator)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
break
|
break
|
||||||
try:
|
with common.ignored(AttributeError):
|
||||||
if str(call.name) == 'else':
|
if str(call.name) == 'else':
|
||||||
break
|
break
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
continue
|
continue
|
||||||
result += follow_call(call)
|
result += follow_call(call)
|
||||||
elif call == '*':
|
elif call == '*':
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ instantiated. This class represents these cases.
|
|||||||
So, why is there also a ``Class`` class here? Well, there are decorators and
|
So, why is there also a ``Class`` class here? Well, there are decorators and
|
||||||
they change classes in Python 3.
|
they change classes in Python 3.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
@@ -61,10 +63,8 @@ class Instance(use_metaclass(cache.CachedMetaClass, Executable)):
|
|||||||
else:
|
else:
|
||||||
# need to execute the __init__ function, because the dynamic param
|
# need to execute the __init__ function, because the dynamic param
|
||||||
# searching needs it.
|
# searching needs it.
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
self.execute_subscope_by_name('__init__', self.var_args)
|
self.execute_subscope_by_name('__init__', self.var_args)
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
# Generated instances are classes that are just generated by self
|
# Generated instances are classes that are just generated by self
|
||||||
# (No var_args) used.
|
# (No var_args) used.
|
||||||
self.is_generated = False
|
self.is_generated = False
|
||||||
@@ -769,9 +769,13 @@ class Generator(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
|||||||
debug.warning('Tried to get array access on a generator', self)
|
debug.warning('Tried to get array access on a generator', self)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@property
|
def __getattr__(self, name):
|
||||||
def parent(self):
|
if name not in ['start_pos', 'end_pos', 'parent', 'get_imports',
|
||||||
return self.func.parent
|
'asserts', 'doc', 'docstr', 'get_parent_until', 'get_code',
|
||||||
|
'subscopes']:
|
||||||
|
raise AttributeError("Accessing %s of %s is not allowed."
|
||||||
|
% (self, name))
|
||||||
|
return getattr(self.func, name)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s of %s>" % (type(self).__name__, self.func)
|
return "<%s of %s>" % (type(self).__name__, self.func)
|
||||||
@@ -800,10 +804,8 @@ class Array(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
|||||||
if isinstance(index, Instance) \
|
if isinstance(index, Instance) \
|
||||||
and str(index.name) in ['int', 'str'] \
|
and str(index.name) in ['int', 'str'] \
|
||||||
and len(index.var_args) == 1:
|
and len(index.var_args) == 1:
|
||||||
try:
|
with common.ignored(KeyError, IndexError):
|
||||||
return self.get_exact_index_types(index.var_args[0])
|
return self.get_exact_index_types(index.var_args[0])
|
||||||
except (KeyError, IndexError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
result = list(self._follow_values(self._array.values))
|
result = list(self._follow_values(self._array.values))
|
||||||
result += dynamic.check_array_additions(self)
|
result += dynamic.check_array_additions(self)
|
||||||
|
|||||||
+5
-6
@@ -1,5 +1,8 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
|
from jedi import common
|
||||||
from jedi import parsing_representation as pr
|
from jedi import parsing_representation as pr
|
||||||
|
|
||||||
|
|
||||||
@@ -21,13 +24,11 @@ def fast_parent_copy(obj):
|
|||||||
|
|
||||||
before = ()
|
before = ()
|
||||||
for cls in new_obj.__class__.__mro__:
|
for cls in new_obj.__class__.__mro__:
|
||||||
try:
|
with common.ignored(AttributeError):
|
||||||
if before == cls.__slots__:
|
if before == cls.__slots__:
|
||||||
continue
|
continue
|
||||||
before = cls.__slots__
|
before = cls.__slots__
|
||||||
items += [(n, getattr(new_obj, n)) for n in before]
|
items += [(n, getattr(new_obj, n)) for n in before]
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
for key, value in items:
|
for key, value in items:
|
||||||
# replace parent (first try _parent and then parent)
|
# replace parent (first try _parent and then parent)
|
||||||
@@ -35,10 +36,8 @@ def fast_parent_copy(obj):
|
|||||||
if key == 'parent' and '_parent' in items:
|
if key == 'parent' and '_parent' in items:
|
||||||
# parent can be a property
|
# parent can be a property
|
||||||
continue
|
continue
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
setattr(new_obj, key, new_elements[value])
|
setattr(new_obj, key, new_elements[value])
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
elif key in ['parent_function', 'use_as_parent', '_sub_module']:
|
elif key in ['parent_function', 'use_as_parent', '_sub_module']:
|
||||||
continue
|
continue
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
|
|||||||
+19
-17
@@ -5,23 +5,22 @@ any actual importing done. This module is about finding modules in the
|
|||||||
filesystem. This can be quite tricky sometimes, because Python imports are not
|
filesystem. This can be quite tricky sometimes, because Python imports are not
|
||||||
always that simple.
|
always that simple.
|
||||||
|
|
||||||
Currently the import process uses ``imp`` to find modules. In the future, it's
|
This module uses imp for python up to 3.2 and importlib for python 3.3 on; the
|
||||||
a goal to use ``importlib`` for this purpose. There's a `pull request
|
correct implementation is delegated to _compatibility.
|
||||||
<https://github.com/davidhalter/jedi/pull/109>`_ for that.
|
|
||||||
|
|
||||||
This module also supports import autocompletion, which means to complete
|
This module also supports import autocompletion, which means to complete
|
||||||
statements like ``from datetim`` (curser at the end would return ``datetime``).
|
statements like ``from datetim`` (curser at the end would return ``datetime``).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import imp
|
|
||||||
import sys
|
import sys
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
|
from jedi._compatibility import find_module
|
||||||
from jedi import modules
|
from jedi import modules
|
||||||
|
from jedi import common
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import parsing_representation as pr
|
from jedi import parsing_representation as pr
|
||||||
from jedi import cache
|
from jedi import cache
|
||||||
@@ -123,11 +122,9 @@ class ImportPath(pr.Base):
|
|||||||
|
|
||||||
if self.import_stmt.relative_count:
|
if self.import_stmt.relative_count:
|
||||||
rel_path = self.get_relative_path() + '/__init__.py'
|
rel_path = self.get_relative_path() + '/__init__.py'
|
||||||
try:
|
with common.ignored(IOError):
|
||||||
m = modules.Module(rel_path)
|
m = modules.Module(rel_path)
|
||||||
names += m.parser.module.get_defined_names()
|
names += m.parser.module.get_defined_names()
|
||||||
except IOError:
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
if on_import_stmt and isinstance(scope, pr.Module) \
|
if on_import_stmt and isinstance(scope, pr.Module) \
|
||||||
and scope.path.endswith('__init__.py'):
|
and scope.path.endswith('__init__.py'):
|
||||||
@@ -238,20 +235,22 @@ class ImportPath(pr.Base):
|
|||||||
|
|
||||||
global imports_processed
|
global imports_processed
|
||||||
imports_processed += 1
|
imports_processed += 1
|
||||||
|
importing = None
|
||||||
if path is not None:
|
if path is not None:
|
||||||
return imp.find_module(string, [path])
|
importing = find_module(string, [path])
|
||||||
else:
|
else:
|
||||||
debug.dbg('search_module', string, self.file_path)
|
debug.dbg('search_module', string, self.file_path)
|
||||||
# Override the sys.path. It works only good that way.
|
# Override the sys.path. It works only good that way.
|
||||||
# Injecting the path directly into `find_module` did not work.
|
# Injecting the path directly into `find_module` did not work.
|
||||||
sys.path, temp = sys_path_mod, sys.path
|
sys.path, temp = sys_path_mod, sys.path
|
||||||
try:
|
try:
|
||||||
i = imp.find_module(string)
|
importing = find_module(string)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
sys.path = temp
|
sys.path = temp
|
||||||
raise
|
raise
|
||||||
sys.path = temp
|
sys.path = temp
|
||||||
return i
|
|
||||||
|
return importing
|
||||||
|
|
||||||
if self.file_path:
|
if self.file_path:
|
||||||
sys_path_mod = list(self.sys_path_with_modifications())
|
sys_path_mod = list(self.sys_path_with_modifications())
|
||||||
@@ -259,6 +258,9 @@ class ImportPath(pr.Base):
|
|||||||
else:
|
else:
|
||||||
sys_path_mod = list(modules.get_sys_path())
|
sys_path_mod = list(modules.get_sys_path())
|
||||||
|
|
||||||
|
def module_not_found():
|
||||||
|
raise ModuleNotFound('The module you searched has not been found')
|
||||||
|
|
||||||
current_namespace = (None, None, None)
|
current_namespace = (None, None, None)
|
||||||
# now execute those paths
|
# now execute those paths
|
||||||
rest = []
|
rest = []
|
||||||
@@ -270,19 +272,19 @@ class ImportPath(pr.Base):
|
|||||||
and len(self.import_path) == 1:
|
and len(self.import_path) == 1:
|
||||||
# follow `from . import some_variable`
|
# follow `from . import some_variable`
|
||||||
rel_path = self.get_relative_path()
|
rel_path = self.get_relative_path()
|
||||||
try:
|
with common.ignored(ImportError):
|
||||||
current_namespace = follow_str(rel_path, '__init__')
|
current_namespace = follow_str(rel_path, '__init__')
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
if current_namespace[1]:
|
if current_namespace[1]:
|
||||||
rest = self.import_path[i:]
|
rest = self.import_path[i:]
|
||||||
else:
|
else:
|
||||||
raise ModuleNotFound(
|
module_not_found()
|
||||||
'The module you searched has not been found')
|
|
||||||
|
if current_namespace == (None, None, False):
|
||||||
|
module_not_found()
|
||||||
|
|
||||||
sys_path_mod.pop(0) # TODO why is this here?
|
sys_path_mod.pop(0) # TODO why is this here?
|
||||||
path = current_namespace[1]
|
path = current_namespace[1]
|
||||||
is_package_directory = current_namespace[2][2] == imp.PKG_DIRECTORY
|
is_package_directory = current_namespace[2]
|
||||||
|
|
||||||
f = None
|
f = None
|
||||||
if is_package_directory or current_namespace[0]:
|
if is_package_directory or current_namespace[0]:
|
||||||
|
|||||||
+4
-3
@@ -1,7 +1,10 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
import pydoc
|
import pydoc
|
||||||
import keyword
|
import keyword
|
||||||
|
|
||||||
from jedi._compatibility import is_py3k
|
from jedi._compatibility import is_py3k
|
||||||
|
from jedi import common
|
||||||
import builtin
|
import builtin
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -63,12 +66,10 @@ def imitate_pydoc(string):
|
|||||||
# with unicode strings)
|
# with unicode strings)
|
||||||
string = str(string)
|
string = str(string)
|
||||||
h = pydoc.help
|
h = pydoc.help
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
# try to access symbols
|
# try to access symbols
|
||||||
string = h.symbols[string]
|
string = h.symbols[string]
|
||||||
string, _, related = string.partition(' ')
|
string, _, related = string.partition(' ')
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
get_target = lambda s: h.topics.get(s, h.keywords.get(s))
|
get_target = lambda s: h.topics.get(s, h.keywords.get(s))
|
||||||
while isinstance(string, str):
|
while isinstance(string, str):
|
||||||
|
|||||||
+4
-9
@@ -27,6 +27,7 @@ from jedi import parsing_representation as pr
|
|||||||
from jedi import fast_parser
|
from jedi import fast_parser
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
|
from jedi import common
|
||||||
|
|
||||||
|
|
||||||
class CachedModule(object):
|
class CachedModule(object):
|
||||||
@@ -107,11 +108,9 @@ class ModuleWithCursor(Module):
|
|||||||
def parser(self):
|
def parser(self):
|
||||||
""" get the parser lazy """
|
""" get the parser lazy """
|
||||||
if not self._parser:
|
if not self._parser:
|
||||||
try:
|
with common.ignored(KeyError):
|
||||||
parser = cache.parser_cache[self.path].parser
|
parser = cache.parser_cache[self.path].parser
|
||||||
cache.invalidate_star_import_cache(parser.module)
|
cache.invalidate_star_import_cache(parser.module)
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
# Call the parser already here, because it will be used anyways.
|
# Call the parser already here, because it will be used anyways.
|
||||||
# Also, the position is here important (which will not be used by
|
# Also, the position is here important (which will not be used by
|
||||||
# default), therefore fill the cache here.
|
# default), therefore fill the cache here.
|
||||||
@@ -348,10 +347,8 @@ def sys_path_with_modifications(module):
|
|||||||
return [] # support for modules without a path is intentionally bad.
|
return [] # support for modules without a path is intentionally bad.
|
||||||
|
|
||||||
curdir = os.path.abspath(os.curdir)
|
curdir = os.path.abspath(os.curdir)
|
||||||
try:
|
with common.ignored(OSError):
|
||||||
os.chdir(os.path.dirname(module.path))
|
os.chdir(os.path.dirname(module.path))
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
result = check_module(module)
|
result = check_module(module)
|
||||||
result += detect_django_path(module.path)
|
result += detect_django_path(module.path)
|
||||||
@@ -372,12 +369,10 @@ def detect_django_path(module_path):
|
|||||||
else:
|
else:
|
||||||
module_path = new
|
module_path = new
|
||||||
|
|
||||||
try:
|
with common.ignored(IOError):
|
||||||
with open(module_path + os.path.sep + 'manage.py'):
|
with open(module_path + os.path.sep + 'manage.py'):
|
||||||
debug.dbg('Found django path: %s' % module_path)
|
debug.dbg('Found django path: %s' % module_path)
|
||||||
result.append(module_path)
|
result.append(module_path)
|
||||||
except IOError:
|
|
||||||
pass
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -15,6 +15,7 @@ within the statement. This lowers memory usage and cpu time and reduces the
|
|||||||
complexity of the ``Parser`` (there's another parser sitting inside
|
complexity of the ``Parser`` (there's another parser sitting inside
|
||||||
``Statement``, which produces ``Array`` and ``Call``).
|
``Statement``, which produces ``Array`` and ``Call``).
|
||||||
"""
|
"""
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
import tokenize
|
import tokenize
|
||||||
import keyword
|
import keyword
|
||||||
@@ -396,7 +397,7 @@ class Parser(object):
|
|||||||
self._check_user_stmt(stmt)
|
self._check_user_stmt(stmt)
|
||||||
|
|
||||||
# Attribute docstring (PEP 257) support
|
# Attribute docstring (PEP 257) support
|
||||||
try:
|
with common.ignored(IndexError, AttributeError):
|
||||||
# If string literal is being parsed
|
# If string literal is being parsed
|
||||||
first_tok = stmt.token_list[0]
|
first_tok = stmt.token_list[0]
|
||||||
if (not stmt.set_vars and
|
if (not stmt.set_vars and
|
||||||
@@ -405,8 +406,6 @@ class Parser(object):
|
|||||||
first_tok[0] == tokenize.STRING):
|
first_tok[0] == tokenize.STRING):
|
||||||
# ... then set it as a docstring
|
# ... then set it as a docstring
|
||||||
self.scope.statements[-1].add_docstr(first_tok[1])
|
self.scope.statements[-1].add_docstr(first_tok[1])
|
||||||
except (IndexError, AttributeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if tok in always_break + not_first_break:
|
if tok in always_break + not_first_break:
|
||||||
self._gen.push_last_back()
|
self._gen.push_last_back()
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ statements in this scope. Check this out:
|
|||||||
See also :attr:`Scope.subscopes` and :attr:`Scope.statements`.
|
See also :attr:`Scope.subscopes` and :attr:`Scope.statements`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -560,8 +561,10 @@ class Flow(Scope):
|
|||||||
@parent.setter
|
@parent.setter
|
||||||
def parent(self, value):
|
def parent(self, value):
|
||||||
self._parent = value
|
self._parent = value
|
||||||
if self.next:
|
try:
|
||||||
self.next.parent = value
|
self.next.parent = value
|
||||||
|
except AttributeError:
|
||||||
|
return
|
||||||
|
|
||||||
def get_code(self, first_indent=False, indention=' '):
|
def get_code(self, first_indent=False, indention=' '):
|
||||||
stmts = []
|
stmts = []
|
||||||
@@ -1278,11 +1281,8 @@ class Array(Call):
|
|||||||
inner = []
|
inner = []
|
||||||
for i, stmt in enumerate(self.values):
|
for i, stmt in enumerate(self.values):
|
||||||
s = ''
|
s = ''
|
||||||
try:
|
with common.ignored(IndexError):
|
||||||
key = self.keys[i]
|
key = self.keys[i]
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
s += key.get_code(new_line=False) + ': '
|
s += key.get_code(new_line=False) + ': '
|
||||||
s += stmt.get_code(new_line=False)
|
s += stmt.get_code(new_line=False)
|
||||||
inner.append(s)
|
inner.append(s)
|
||||||
|
|||||||
+2
-5
@@ -12,11 +12,11 @@ following functions (sometimes bug-prone):
|
|||||||
- extract variable
|
- extract variable
|
||||||
- inline variable
|
- inline variable
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
import difflib
|
import difflib
|
||||||
|
|
||||||
|
from jedi import common
|
||||||
from jedi import modules
|
from jedi import modules
|
||||||
from jedi import helpers
|
from jedi import helpers
|
||||||
from jedi import parsing_representation as pr
|
from jedi import parsing_representation as pr
|
||||||
@@ -168,7 +168,7 @@ def inline(script):
|
|||||||
dct = {}
|
dct = {}
|
||||||
|
|
||||||
definitions = script.goto()
|
definitions = script.goto()
|
||||||
try:
|
with common.ignored(AssertionError):
|
||||||
assert len(definitions) == 1
|
assert len(definitions) == 1
|
||||||
stmt = definitions[0].definition
|
stmt = definitions[0].definition
|
||||||
related_names = script.related_names()
|
related_names = script.related_names()
|
||||||
@@ -202,7 +202,4 @@ def inline(script):
|
|||||||
else:
|
else:
|
||||||
new_lines.pop(index)
|
new_lines.pop(index)
|
||||||
|
|
||||||
except AssertionError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return Refactoring(dct)
|
return Refactoring(dct)
|
||||||
|
|||||||
@@ -31,7 +31,13 @@ setup(name='jedi',
|
|||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
|
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python :: 2',
|
||||||
|
'Programming Language :: Python :: 2.5',
|
||||||
|
'Programming Language :: Python :: 2.6',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.2',
|
||||||
|
'Programming Language :: Python :: 3.3',
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||||
'Topic :: Text Editors :: Integrated Development Environments (IDE)',
|
'Topic :: Text Editors :: Integrated Development Environments (IDE)',
|
||||||
'Topic :: Utilities',
|
'Topic :: Utilities',
|
||||||
|
|||||||
@@ -89,12 +89,3 @@ def huhu(db):
|
|||||||
"""
|
"""
|
||||||
#? sqlite3.Connection()
|
#? sqlite3.Connection()
|
||||||
db
|
db
|
||||||
|
|
||||||
# -----------------
|
|
||||||
# various regression tests
|
|
||||||
# -----------------
|
|
||||||
|
|
||||||
#62
|
|
||||||
import threading
|
|
||||||
#? ['_Verbose', '_VERBOSE']
|
|
||||||
threading._Verbose
|
|
||||||
|
|||||||
@@ -65,6 +65,18 @@ def pytest_generate_tests(metafunc):
|
|||||||
refactor.collect_dir_tests(base_dir, test_files))
|
refactor.collect_dir_tests(base_dir, test_files))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def isolated_jedi_cache(monkeypatch, tmpdir):
|
||||||
|
"""
|
||||||
|
Set `jedi.settings.cache_directory` to a temporary directory during test.
|
||||||
|
|
||||||
|
Same as `clean_jedi_cache`, but create the temporary directory for
|
||||||
|
each test case (scope='function').
|
||||||
|
"""
|
||||||
|
settings = base.jedi.settings
|
||||||
|
monkeypatch.setattr(settings, 'cache_directory', str(tmpdir))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
def clean_jedi_cache(request):
|
def clean_jedi_cache(request):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ def make_definitions():
|
|||||||
definitions += api.defined_names(source)
|
definitions += api.defined_names(source)
|
||||||
|
|
||||||
source += textwrap.dedent("""
|
source += textwrap.dedent("""
|
||||||
variable = sys or C or x or f or g or h""")
|
variable = sys or C or x or f or g or g() or h""")
|
||||||
lines = source.splitlines()
|
lines = source.splitlines()
|
||||||
script = api.Script(source, len(lines), len('variable'), None)
|
script = api.Script(source, len(lines), len('variable'), None)
|
||||||
definitions += script.definition()
|
definitions += script.definition()
|
||||||
@@ -50,4 +50,4 @@ def make_definitions():
|
|||||||
@pytest.mark.parametrize('definition', make_definitions())
|
@pytest.mark.parametrize('definition', make_definitions())
|
||||||
def test_basedefinition_type(definition):
|
def test_basedefinition_type(definition):
|
||||||
assert definition.type in ('module', 'class', 'instance', 'function',
|
assert definition.type in ('module', 'class', 'instance', 'function',
|
||||||
'statement', 'import', 'param')
|
'generator', 'statement', 'import', 'param')
|
||||||
|
|||||||
+26
-2
@@ -1,3 +1,5 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi.cache import ParserCacheItem, _ModulePickling
|
from jedi.cache import ParserCacheItem, _ModulePickling
|
||||||
|
|
||||||
@@ -21,10 +23,32 @@ def test_modulepickling_change_cache_dir(monkeypatch, tmpdir):
|
|||||||
|
|
||||||
monkeypatch.setattr(settings, 'cache_directory', dir_1)
|
monkeypatch.setattr(settings, 'cache_directory', dir_1)
|
||||||
ModulePickling.save_module(path_1, item_1)
|
ModulePickling.save_module(path_1, item_1)
|
||||||
cached = ModulePickling.load_module(path_1, item_1.change_time - 1)
|
cached = load_stored_item(ModulePickling, path_1, item_1)
|
||||||
assert cached == item_1.parser
|
assert cached == item_1.parser
|
||||||
|
|
||||||
monkeypatch.setattr(settings, 'cache_directory', dir_2)
|
monkeypatch.setattr(settings, 'cache_directory', dir_2)
|
||||||
ModulePickling.save_module(path_2, item_2)
|
ModulePickling.save_module(path_2, item_2)
|
||||||
cached = ModulePickling.load_module(path_1, item_1.change_time - 1)
|
cached = load_stored_item(ModulePickling, path_1, item_1)
|
||||||
assert cached is None
|
assert cached is None
|
||||||
|
|
||||||
|
|
||||||
|
def load_stored_item(cache, path, item):
|
||||||
|
"""Load `item` stored at `path` in `cache`."""
|
||||||
|
return cache.load_module(path, item.change_time - 1)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("isolated_jedi_cache")
|
||||||
|
def test_modulepickling_delete_incompatible_cache():
|
||||||
|
item = ParserCacheItem('fake parser')
|
||||||
|
path = 'fake path'
|
||||||
|
|
||||||
|
cache1 = _ModulePickling()
|
||||||
|
cache1.version = 1
|
||||||
|
cache1.save_module(path, item)
|
||||||
|
cached1 = load_stored_item(cache1, path, item)
|
||||||
|
assert cached1 == item.parser
|
||||||
|
|
||||||
|
cache2 = _ModulePickling()
|
||||||
|
cache2.version = 2
|
||||||
|
cached2 = load_stored_item(cache2, path, item)
|
||||||
|
assert cached2 is None
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = py25, py26, py27, py32
|
envlist = py25, py26, py27, py32, py33
|
||||||
[testenv]
|
[testenv]
|
||||||
deps =
|
deps =
|
||||||
pytest
|
pytest
|
||||||
@@ -14,3 +14,9 @@ deps =
|
|||||||
deps =
|
deps =
|
||||||
unittest2
|
unittest2
|
||||||
{[testenv]deps}
|
{[testenv]deps}
|
||||||
|
[testenv:cov]
|
||||||
|
deps =
|
||||||
|
pytest-cov
|
||||||
|
{[testenv]deps}
|
||||||
|
commands =
|
||||||
|
py.test --cov jedi []
|
||||||
|
|||||||
Reference in New Issue
Block a user