forked from VimPlug/jedi
dev/refactor merge
This commit is contained in:
46
jedi/api.py
46
jedi/api.py
@@ -11,6 +11,7 @@ __all__ = ['Script', 'NotFoundError', 'set_debug_function', '_quick_complete']
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
import warnings
|
||||||
|
|
||||||
import parsing
|
import parsing
|
||||||
import parsing_representation as pr
|
import parsing_representation as pr
|
||||||
@@ -121,8 +122,8 @@ class Script(object):
|
|||||||
for c in names:
|
for c in names:
|
||||||
completions.append((c, s))
|
completions.append((c, s))
|
||||||
|
|
||||||
if not dot: # named_params have no dots
|
if not dot: # named params have no dots
|
||||||
call_def = self.get_in_function_call()
|
call_def = self.function_definition()
|
||||||
if call_def:
|
if call_def:
|
||||||
if not call_def.module.is_builtin():
|
if not call_def.module.is_builtin():
|
||||||
for p in call_def.params:
|
for p in call_def.params:
|
||||||
@@ -169,7 +170,7 @@ class Script(object):
|
|||||||
x.word.lower()))
|
x.word.lower()))
|
||||||
|
|
||||||
def _prepare_goto(self, goto_path, is_like_search=False):
|
def _prepare_goto(self, goto_path, is_like_search=False):
|
||||||
""" Base for complete, goto and get_definition. Basically it returns
|
""" Base for complete, goto and definition. Basically it returns
|
||||||
the resolved scopes under cursor. """
|
the resolved scopes under cursor. """
|
||||||
debug.dbg('start: %s in %s' % (goto_path, self._parser.user_scope))
|
debug.dbg('start: %s in %s' % (goto_path, self._parser.user_scope))
|
||||||
|
|
||||||
@@ -190,21 +191,30 @@ class Script(object):
|
|||||||
return scopes
|
return scopes
|
||||||
|
|
||||||
def _get_under_cursor_stmt(self, cursor_txt):
|
def _get_under_cursor_stmt(self, cursor_txt):
|
||||||
r = parsing.Parser(cursor_txt, no_docstr=True)
|
offset = self.pos[0] - 1, self.pos[1]
|
||||||
|
r = parsing.Parser(cursor_txt, no_docstr=True, offset=offset)
|
||||||
try:
|
try:
|
||||||
stmt = r.module.statements[0]
|
stmt = r.module.statements[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise NotFoundError()
|
raise NotFoundError()
|
||||||
stmt.start_pos = self.pos
|
|
||||||
stmt.parent = self._parser.user_scope
|
stmt.parent = self._parser.user_scope
|
||||||
return stmt
|
return stmt
|
||||||
|
|
||||||
def get_definition(self):
|
def get_definition(self):
|
||||||
|
"""
|
||||||
|
.. deprecated:: 0.5.0
|
||||||
|
Use :attr:`.function_definition` instead.
|
||||||
|
.. todo:: Remove!
|
||||||
|
"""
|
||||||
|
warnings.warn("Use line instead.", DeprecationWarning)
|
||||||
|
return self.definition()
|
||||||
|
|
||||||
|
def definition(self):
|
||||||
"""
|
"""
|
||||||
Return the definitions of a the path under the cursor. This is not a
|
Return the definitions of a the path under the cursor. This is not a
|
||||||
goto function! This follows complicated paths and returns the end, not
|
goto function! This follows complicated paths and returns the end, not
|
||||||
the first definition. The big difference between :meth:`goto` and
|
the first definition. The big difference between :meth:`goto` and
|
||||||
:meth:`get_definition` is that :meth:`goto` doesn't follow imports and
|
:meth:`definition` is that :meth:`goto` doesn't follow imports and
|
||||||
statements. Multiple objects may be returned, because Python itself is
|
statements. Multiple objects may be returned, because Python itself is
|
||||||
a dynamic language, which means depending on an option you can have two
|
a dynamic language, which means depending on an option you can have two
|
||||||
different versions of a function.
|
different versions of a function.
|
||||||
@@ -294,13 +304,13 @@ class Script(object):
|
|||||||
defs, search_name = evaluate.goto(stmt)
|
defs, search_name = evaluate.goto(stmt)
|
||||||
definitions = follow_inexistent_imports(defs)
|
definitions = follow_inexistent_imports(defs)
|
||||||
if isinstance(user_stmt, pr.Statement):
|
if isinstance(user_stmt, pr.Statement):
|
||||||
if user_stmt.get_assignment_calls().start_pos > self.pos:
|
if user_stmt.get_commands()[0].start_pos > self.pos:
|
||||||
# The cursor must be after the start, otherwise the
|
# The cursor must be after the start, otherwise the
|
||||||
# statement is just an assignee.
|
# statement is just an assignee.
|
||||||
definitions = [user_stmt]
|
definitions = [user_stmt]
|
||||||
return definitions, search_name
|
return definitions, search_name
|
||||||
|
|
||||||
def related_names(self, additional_module_paths=[]):
|
def related_names(self, additional_module_paths=()):
|
||||||
"""
|
"""
|
||||||
Return :class:`api_classes.RelatedName` objects, which contain all
|
Return :class:`api_classes.RelatedName` objects, which contain all
|
||||||
names that point to the definition of the name under the cursor. This
|
names that point to the definition of the name under the cursor. This
|
||||||
@@ -314,7 +324,7 @@ class Script(object):
|
|||||||
user_stmt = self._parser.user_stmt
|
user_stmt = self._parser.user_stmt
|
||||||
definitions, search_name = self._goto(add_import_name=True)
|
definitions, search_name = self._goto(add_import_name=True)
|
||||||
if isinstance(user_stmt, pr.Statement) \
|
if isinstance(user_stmt, pr.Statement) \
|
||||||
and self.pos < user_stmt.get_assignment_calls().start_pos:
|
and self.pos < user_stmt.get_commands()[0].start_pos:
|
||||||
# the search_name might be before `=`
|
# the search_name might be before `=`
|
||||||
definitions = [v for v in user_stmt.set_vars
|
definitions = [v for v in user_stmt.set_vars
|
||||||
if unicode(v.names[-1]) == search_name]
|
if unicode(v.names[-1]) == search_name]
|
||||||
@@ -336,6 +346,15 @@ class Script(object):
|
|||||||
return sorted(set(names), key=lambda x: (x.module_path, x.start_pos))
|
return sorted(set(names), key=lambda x: (x.module_path, x.start_pos))
|
||||||
|
|
||||||
def get_in_function_call(self):
|
def get_in_function_call(self):
|
||||||
|
"""
|
||||||
|
.. deprecated:: 0.5.0
|
||||||
|
Use :attr:`.function_definition` instead.
|
||||||
|
.. todo:: Remove!
|
||||||
|
"""
|
||||||
|
warnings.warn("Use line instead.", DeprecationWarning)
|
||||||
|
return self.function_definition()
|
||||||
|
|
||||||
|
def function_definition(self):
|
||||||
"""
|
"""
|
||||||
Return the function object of the call you're currently in.
|
Return the function object of the call you're currently in.
|
||||||
|
|
||||||
@@ -355,9 +374,8 @@ class Script(object):
|
|||||||
if user_stmt is None \
|
if user_stmt is None \
|
||||||
or not isinstance(user_stmt, pr.Statement):
|
or not isinstance(user_stmt, pr.Statement):
|
||||||
return None, 0
|
return None, 0
|
||||||
ass = helpers.fast_parent_copy(user_stmt.get_assignment_calls())
|
|
||||||
|
|
||||||
call, index, stop = helpers.search_function_call(ass, self.pos)
|
call, index, stop = helpers.search_function_definition(user_stmt, self.pos)
|
||||||
return call, index
|
return call, index
|
||||||
|
|
||||||
def check_cache():
|
def check_cache():
|
||||||
@@ -392,7 +410,7 @@ class Script(object):
|
|||||||
|
|
||||||
debug.speed('func_call start')
|
debug.speed('func_call start')
|
||||||
call = None
|
call = None
|
||||||
if settings.use_get_in_function_call_cache:
|
if settings.use_function_definition_cache:
|
||||||
try:
|
try:
|
||||||
call, index = check_cache()
|
call, index = check_cache()
|
||||||
except NotFoundError:
|
except NotFoundError:
|
||||||
@@ -406,9 +424,9 @@ class Script(object):
|
|||||||
return None
|
return None
|
||||||
debug.speed('func_call parsed')
|
debug.speed('func_call parsed')
|
||||||
|
|
||||||
with common.scale_speed_settings(settings.scale_get_in_function_call):
|
with common.scale_speed_settings(settings.scale_function_definition):
|
||||||
_callable = lambda: evaluate.follow_call(call)
|
_callable = lambda: evaluate.follow_call(call)
|
||||||
origins = cache.cache_get_in_function_call(_callable, user_stmt)
|
origins = cache.cache_function_definition(_callable, user_stmt)
|
||||||
debug.speed('func_call followed')
|
debug.speed('func_call followed')
|
||||||
|
|
||||||
if len(origins) == 0:
|
if len(origins) == 0:
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ class Completion(BaseDefinition):
|
|||||||
class Definition(BaseDefinition):
|
class Definition(BaseDefinition):
|
||||||
"""
|
"""
|
||||||
*Definition* objects are returned from :meth:`api.Script.goto` or
|
*Definition* objects are returned from :meth:`api.Script.goto` or
|
||||||
:meth:`api.Script.get_definition`.
|
:meth:`api.Script.definition`.
|
||||||
"""
|
"""
|
||||||
def __init__(self, definition):
|
def __init__(self, definition):
|
||||||
super(Definition, self).__init__(definition, definition.start_pos)
|
super(Definition, self).__init__(definition, definition.start_pos)
|
||||||
@@ -339,9 +339,11 @@ class RelatedName(BaseDefinition):
|
|||||||
|
|
||||||
|
|
||||||
class CallDef(object):
|
class CallDef(object):
|
||||||
""" `CallDef` objects is the return value of `Script.get_in_function_call`.
|
"""
|
||||||
|
`CallDef` objects is the return value of `Script.function_definition`.
|
||||||
It knows what functions you are currently in. e.g. `isinstance(` would
|
It knows what functions you are currently in. e.g. `isinstance(` would
|
||||||
return the `isinstance` function. without `(` it would return nothing."""
|
return the `isinstance` function. without `(` it would return nothing.
|
||||||
|
"""
|
||||||
def __init__(self, executable, index, call):
|
def __init__(self, executable, index, call):
|
||||||
self.executable = executable
|
self.executable = executable
|
||||||
self.index = index
|
self.index = index
|
||||||
|
|||||||
@@ -135,8 +135,8 @@ def time_cache(time_add_setting):
|
|||||||
return _temp
|
return _temp
|
||||||
|
|
||||||
|
|
||||||
@time_cache("get_in_function_call_validity")
|
@time_cache("function_definition_validity")
|
||||||
def cache_get_in_function_call(stmt):
|
def cache_function_definition(stmt):
|
||||||
module_path = stmt.get_parent_until().path
|
module_path = stmt.get_parent_until().path
|
||||||
return None if module_path is None else (module_path, stmt.start_pos)
|
return None if module_path is None else (module_path, stmt.start_pos)
|
||||||
|
|
||||||
|
|||||||
@@ -56,13 +56,14 @@ class PushBackIterator(object):
|
|||||||
|
|
||||||
|
|
||||||
class NoErrorTokenizer(object):
|
class NoErrorTokenizer(object):
|
||||||
def __init__(self, readline, line_offset=0, stop_on_scope=False):
|
def __init__(self, readline, offset=(0, 0), stop_on_scope=False):
|
||||||
self.readline = readline
|
self.readline = readline
|
||||||
self.gen = PushBackIterator(tokenize.generate_tokens(readline))
|
self.gen = PushBackIterator(tokenize.generate_tokens(readline))
|
||||||
self.line_offset = line_offset
|
self.offset = offset
|
||||||
self.stop_on_scope = stop_on_scope
|
self.stop_on_scope = stop_on_scope
|
||||||
self.first_scope = False
|
self.first_scope = False
|
||||||
self.closed = False
|
self.closed = False
|
||||||
|
self.first = True
|
||||||
|
|
||||||
def push_last_back(self):
|
def push_last_back(self):
|
||||||
self.gen.push_back(self.current)
|
self.gen.push_back(self.current)
|
||||||
@@ -90,7 +91,8 @@ class NoErrorTokenizer(object):
|
|||||||
debug.warning('indentation error on line %s, ignoring it' %
|
debug.warning('indentation error on line %s, ignoring it' %
|
||||||
self.current[2][0])
|
self.current[2][0])
|
||||||
# add the starting line of the last position
|
# add the starting line of the last position
|
||||||
self.line_offset += self.current[2][0]
|
self.offset = (self.offset[0] + self.current[2][0],
|
||||||
|
self.current[2][1])
|
||||||
self.gen = PushBackIterator(tokenize.generate_tokens(
|
self.gen = PushBackIterator(tokenize.generate_tokens(
|
||||||
self.readline))
|
self.readline))
|
||||||
return self.__next__()
|
return self.__next__()
|
||||||
@@ -106,8 +108,13 @@ class NoErrorTokenizer(object):
|
|||||||
elif c[1] != '@':
|
elif c[1] != '@':
|
||||||
self.first_scope = True
|
self.first_scope = True
|
||||||
|
|
||||||
c[2] = self.line_offset + c[2][0], c[2][1]
|
if self.first:
|
||||||
c[3] = self.line_offset + c[3][0], c[3][1]
|
c[2] = self.offset[0] + c[2][0], self.offset[1] + c[2][1]
|
||||||
|
c[3] = self.offset[0] + c[3][0], self.offset[1] + c[3][1]
|
||||||
|
self.first = False
|
||||||
|
else:
|
||||||
|
c[2] = self.offset[0] + c[2][0], c[2][1]
|
||||||
|
c[3] = self.offset[0] + c[3][0], c[3][1]
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
168
jedi/dynamic.py
168
jedi/dynamic.py
@@ -21,7 +21,6 @@ import parsing_representation as pr
|
|||||||
import evaluate_representation as er
|
import evaluate_representation as er
|
||||||
import modules
|
import modules
|
||||||
import evaluate
|
import evaluate
|
||||||
import helpers
|
|
||||||
import settings
|
import settings
|
||||||
import debug
|
import debug
|
||||||
import imports
|
import imports
|
||||||
@@ -133,7 +132,7 @@ def search_params(param):
|
|||||||
|
|
||||||
for stmt in possible_stmts:
|
for stmt in possible_stmts:
|
||||||
if not isinstance(stmt, pr.Import):
|
if not isinstance(stmt, pr.Import):
|
||||||
calls = _scan_array(stmt.get_assignment_calls(), func_name)
|
calls = _scan_statement(stmt, func_name)
|
||||||
for c in calls:
|
for c in calls:
|
||||||
# no execution means that params cannot be set
|
# no execution means that params cannot be set
|
||||||
call_path = c.generate_call_path()
|
call_path = c.generate_call_path()
|
||||||
@@ -157,11 +156,12 @@ def search_params(param):
|
|||||||
|
|
||||||
# get the param name
|
# get the param name
|
||||||
if param.assignment_details:
|
if param.assignment_details:
|
||||||
arr = param.assignment_details[0][1]
|
# first assignment details, others would be a syntax error
|
||||||
|
commands, op = param.assignment_details[0]
|
||||||
else:
|
else:
|
||||||
arr = param.get_assignment_calls()
|
commands = param.get_commands()
|
||||||
offset = 1 if arr[0][0] in ['*', '**'] else 0
|
offset = 1 if commands[0] in ['*', '**'] else 0
|
||||||
param_name = str(arr[0][offset].name)
|
param_name = str(commands[offset].name)
|
||||||
|
|
||||||
# add the listener
|
# add the listener
|
||||||
listener = ParamListener()
|
listener = ParamListener()
|
||||||
@@ -182,33 +182,49 @@ def search_params(param):
|
|||||||
|
|
||||||
def check_array_additions(array):
|
def check_array_additions(array):
|
||||||
""" Just a mapper function for the internal _check_array_additions """
|
""" Just a mapper function for the internal _check_array_additions """
|
||||||
if array._array.type not in ['list', 'set']:
|
if not pr.Array.is_type(array._array, pr.Array.LIST, pr.Array.SET):
|
||||||
# TODO also check for dict updates
|
# TODO also check for dict updates
|
||||||
return []
|
return []
|
||||||
|
|
||||||
is_list = array._array.type == 'list'
|
is_list = array._array.type == 'list'
|
||||||
current_module = array._array.parent_stmt.get_parent_until()
|
current_module = array._array.get_parent_until()
|
||||||
res = _check_array_additions(array, current_module, is_list)
|
res = _check_array_additions(array, current_module, is_list)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def _scan_array(arr, search_name):
|
def _scan_statement(stmt, search_name, assignment_details=False):
|
||||||
""" Returns the function Call that match search_name in an Array. """
|
""" Returns the function Call that match search_name in an Array. """
|
||||||
result = []
|
def scan_array(arr, search_name):
|
||||||
for sub in arr:
|
result = []
|
||||||
for s in sub:
|
if arr.type == pr.Array.DICT:
|
||||||
if isinstance(s, pr.Array):
|
for key_stmt, value_stmt in arr.items():
|
||||||
result += _scan_array(s, search_name)
|
result += _scan_statement(key_stmt, search_name)
|
||||||
elif isinstance(s, pr.Call):
|
result += _scan_statement(value_stmt, search_name)
|
||||||
s_new = s
|
else:
|
||||||
while s_new is not None:
|
for stmt in arr:
|
||||||
n = s_new.name
|
result += _scan_statement(stmt, search_name)
|
||||||
if isinstance(n, pr.Name) and search_name in n.names:
|
return result
|
||||||
result.append(s)
|
|
||||||
|
check = list(stmt.get_commands())
|
||||||
|
if assignment_details:
|
||||||
|
for commands, op in stmt.assignment_details:
|
||||||
|
check += commands
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for c in check:
|
||||||
|
if isinstance(c, pr.Array):
|
||||||
|
result += scan_array(c, search_name)
|
||||||
|
elif isinstance(c, pr.Call):
|
||||||
|
s_new = c
|
||||||
|
while s_new is not None:
|
||||||
|
n = s_new.name
|
||||||
|
if isinstance(n, pr.Name) and search_name in n.names:
|
||||||
|
result.append(c)
|
||||||
|
|
||||||
|
if s_new.execution is not None:
|
||||||
|
result += scan_array(s_new.execution, search_name)
|
||||||
|
s_new = s_new.next
|
||||||
|
|
||||||
if s_new.execution is not None:
|
|
||||||
result += _scan_array(s_new.execution, search_name)
|
|
||||||
s_new = s_new.next
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -238,7 +254,7 @@ def _check_array_additions(compare_array, module, is_list):
|
|||||||
backtrack_path = iter(call_path[:separate_index])
|
backtrack_path = iter(call_path[:separate_index])
|
||||||
|
|
||||||
position = c.start_pos
|
position = c.start_pos
|
||||||
scope = c.parent_stmt.parent
|
scope = c.get_parent_until(pr.IsScope)
|
||||||
|
|
||||||
found = evaluate.follow_call_path(backtrack_path, scope, position)
|
found = evaluate.follow_call_path(backtrack_path, scope, position)
|
||||||
if not compare_array in found:
|
if not compare_array in found:
|
||||||
@@ -248,26 +264,28 @@ def _check_array_additions(compare_array, module, is_list):
|
|||||||
if not params.values:
|
if not params.values:
|
||||||
continue # no params: just ignore it
|
continue # no params: just ignore it
|
||||||
if add_name in ['append', 'add']:
|
if add_name in ['append', 'add']:
|
||||||
result += evaluate.follow_call_list(params)
|
for param in params:
|
||||||
|
result += evaluate.follow_statement(param)
|
||||||
elif add_name in ['insert']:
|
elif add_name in ['insert']:
|
||||||
try:
|
try:
|
||||||
second_param = params[1]
|
second_param = params[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
result += evaluate.follow_call_list([second_param])
|
result += evaluate.follow_statement(second_param)
|
||||||
elif add_name in ['extend', 'update']:
|
elif add_name in ['extend', 'update']:
|
||||||
iterators = evaluate.follow_call_list(params)
|
for param in params:
|
||||||
|
iterators = evaluate.follow_statement(param)
|
||||||
result += evaluate.get_iterator_types(iterators)
|
result += evaluate.get_iterator_types(iterators)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_execution_parent(element, *stop_classes):
|
def get_execution_parent(element, *stop_classes):
|
||||||
""" Used to get an Instance/Execution parent """
|
""" Used to get an Instance/Execution parent """
|
||||||
if isinstance(element, er.Array):
|
if isinstance(element, er.Array):
|
||||||
stmt = element._array.parent_stmt
|
stmt = element._array.parent
|
||||||
else:
|
else:
|
||||||
# must be instance
|
# is an Instance with an ArrayInstance inside
|
||||||
stmt = element.var_args.parent_stmt
|
stmt = element.var_args[0].var_args.parent
|
||||||
if isinstance(stmt, er.InstanceElement):
|
if isinstance(stmt, er.InstanceElement):
|
||||||
stop_classes = list(stop_classes) + [er.Function]
|
stop_classes = list(stop_classes) + [er.Function]
|
||||||
return stmt.get_parent_until(stop_classes)
|
return stmt.get_parent_until(stop_classes)
|
||||||
@@ -278,6 +296,7 @@ def _check_array_additions(compare_array, module, is_list):
|
|||||||
search_names = ['append', 'extend', 'insert'] if is_list else \
|
search_names = ['append', 'extend', 'insert'] if is_list else \
|
||||||
['add', 'update']
|
['add', 'update']
|
||||||
comp_arr_parent = get_execution_parent(compare_array, er.Execution)
|
comp_arr_parent = get_execution_parent(compare_array, er.Execution)
|
||||||
|
|
||||||
possible_stmts = []
|
possible_stmts = []
|
||||||
res = []
|
res = []
|
||||||
for n in search_names:
|
for n in search_names:
|
||||||
@@ -303,7 +322,7 @@ def _check_array_additions(compare_array, module, is_list):
|
|||||||
if evaluate.follow_statement.push_stmt(stmt):
|
if evaluate.follow_statement.push_stmt(stmt):
|
||||||
# check recursion
|
# check recursion
|
||||||
continue
|
continue
|
||||||
res += check_calls(_scan_array(stmt.get_assignment_calls(), n), n)
|
res += check_calls(_scan_statement(stmt, n), n)
|
||||||
evaluate.follow_statement.pop_stmt()
|
evaluate.follow_statement.pop_stmt()
|
||||||
# reset settings
|
# reset settings
|
||||||
settings.dynamic_params_for_other_modules = temp_param_add
|
settings.dynamic_params_for_other_modules = temp_param_add
|
||||||
@@ -311,11 +330,11 @@ def _check_array_additions(compare_array, module, is_list):
|
|||||||
|
|
||||||
|
|
||||||
def check_array_instances(instance):
|
def check_array_instances(instance):
|
||||||
""" Used for set() and list() instances. """
|
"""Used for set() and list() instances."""
|
||||||
if not settings.dynamic_arrays_instances:
|
if not settings.dynamic_arrays_instances:
|
||||||
return instance.var_args
|
return instance.var_args
|
||||||
ai = ArrayInstance(instance)
|
ai = ArrayInstance(instance)
|
||||||
return helpers.generate_param_array([ai], instance.var_args.parent_stmt)
|
return [ai]
|
||||||
|
|
||||||
|
|
||||||
class ArrayInstance(pr.Base):
|
class ArrayInstance(pr.Base):
|
||||||
@@ -334,23 +353,24 @@ class ArrayInstance(pr.Base):
|
|||||||
lists/sets are too complicated too handle that.
|
lists/sets are too complicated too handle that.
|
||||||
"""
|
"""
|
||||||
items = []
|
items = []
|
||||||
for array in evaluate.follow_call_list(self.var_args):
|
for stmt in self.var_args:
|
||||||
if isinstance(array, er.Instance) and len(array.var_args):
|
for typ in evaluate.follow_statement(stmt):
|
||||||
temp = array.var_args[0][0]
|
if isinstance(typ, er.Instance) and len(typ.var_args):
|
||||||
if isinstance(temp, ArrayInstance):
|
array = typ.var_args[0]
|
||||||
# prevent recursions
|
if isinstance(array, ArrayInstance):
|
||||||
# TODO compare Modules
|
# prevent recursions
|
||||||
if self.var_args.start_pos != temp.var_args.start_pos:
|
# TODO compare Modules
|
||||||
items += temp.iter_content()
|
if self.var_args.start_pos != array.var_args.start_pos:
|
||||||
else:
|
items += array.iter_content()
|
||||||
debug.warning('ArrayInstance recursion', self.var_args)
|
else:
|
||||||
continue
|
debug.warning('ArrayInstance recursion', self.var_args)
|
||||||
items += evaluate.get_iterator_types([array])
|
continue
|
||||||
|
items += evaluate.get_iterator_types([typ])
|
||||||
|
|
||||||
if self.var_args.parent_stmt is None:
|
if self.var_args.parent is None:
|
||||||
return [] # generated var_args should not be checked for arrays
|
return [] # generated var_args should not be checked for arrays
|
||||||
|
|
||||||
module = self.var_args.parent_stmt.get_parent_until()
|
module = self.var_args.get_parent_until()
|
||||||
is_list = str(self.instance.name) == 'list'
|
is_list = str(self.instance.name) == 'list'
|
||||||
items += _check_array_additions(self.instance, module, is_list)
|
items += _check_array_additions(self.instance, module, is_list)
|
||||||
return items
|
return items
|
||||||
@@ -377,13 +397,13 @@ def related_names(definitions, search_name, mods):
|
|||||||
follow.append(call_path[:i + 1])
|
follow.append(call_path[:i + 1])
|
||||||
|
|
||||||
for f in follow:
|
for f in follow:
|
||||||
follow_res, search = evaluate.goto(call.parent_stmt, f)
|
follow_res, search = evaluate.goto(call.parent, f)
|
||||||
follow_res = related_name_add_import_modules(follow_res, search)
|
follow_res = related_name_add_import_modules(follow_res, search)
|
||||||
|
|
||||||
compare_follow_res = compare_array(follow_res)
|
compare_follow_res = compare_array(follow_res)
|
||||||
# compare to see if they match
|
# compare to see if they match
|
||||||
if any(r in compare_definitions for r in compare_follow_res):
|
if any(r in compare_definitions for r in compare_follow_res):
|
||||||
scope = call.parent_stmt
|
scope = call.parent
|
||||||
result.append(api_classes.RelatedName(search, scope))
|
result.append(api_classes.RelatedName(search, scope))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -416,10 +436,8 @@ def related_names(definitions, search_name, mods):
|
|||||||
if set(f) & set(definitions):
|
if set(f) & set(definitions):
|
||||||
names.append(api_classes.RelatedName(name_part, stmt))
|
names.append(api_classes.RelatedName(name_part, stmt))
|
||||||
else:
|
else:
|
||||||
calls = _scan_array(stmt.get_assignment_calls(), search_name)
|
for call in _scan_statement(stmt, search_name,
|
||||||
for d in stmt.assignment_details:
|
assignment_details=True):
|
||||||
calls += _scan_array(d[1], search_name)
|
|
||||||
for call in calls:
|
|
||||||
names += check_call(call)
|
names += check_call(call)
|
||||||
return names
|
return names
|
||||||
|
|
||||||
@@ -455,39 +473,39 @@ def check_flow_information(flow, search_name, pos):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if isinstance(flow, pr.Flow) and not result:
|
if isinstance(flow, pr.Flow) and not result:
|
||||||
if flow.command in ['if', 'while'] and len(flow.inits) == 1:
|
if flow.command in ['if', 'while'] and len(flow.inputs) == 1:
|
||||||
result = check_statement_information(flow.inits[0], search_name)
|
result = check_statement_information(flow.inputs[0], search_name)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def check_statement_information(stmt, search_name):
|
def check_statement_information(stmt, search_name):
|
||||||
try:
|
try:
|
||||||
ass = stmt.get_assignment_calls()
|
commands = stmt.get_commands()
|
||||||
try:
|
# this might be removed if we analyze and, etc
|
||||||
call = ass.get_only_subelement()
|
assert len(commands) == 1
|
||||||
except AttributeError:
|
call = commands[0]
|
||||||
assert False
|
|
||||||
assert type(call) == pr.Call and str(call.name) == 'isinstance'
|
assert type(call) == pr.Call and str(call.name) == 'isinstance'
|
||||||
assert bool(call.execution)
|
assert bool(call.execution)
|
||||||
|
|
||||||
# isinstance check
|
# isinstance check
|
||||||
isinst = call.execution.values
|
isinst = call.execution.values
|
||||||
assert len(isinst) == 2 # has two params
|
assert len(isinst) == 2 # has two params
|
||||||
assert len(isinst[0]) == 1
|
obj, classes = [stmt.get_commands() for stmt in isinst]
|
||||||
assert len(isinst[1]) == 1
|
assert len(obj) == 1
|
||||||
assert isinstance(isinst[0][0], pr.Call)
|
assert len(classes) == 1
|
||||||
|
assert isinstance(obj[0], pr.Call)
|
||||||
# names fit?
|
# names fit?
|
||||||
assert str(isinst[0][0].name) == search_name
|
assert str(obj[0].name) == search_name
|
||||||
classes_call = isinst[1][0] # class_or_type_or_tuple
|
assert isinstance(classes[0], pr.Call) # can be type or tuple
|
||||||
assert isinstance(classes_call, pr.Call)
|
|
||||||
result = []
|
|
||||||
for c in evaluate.follow_call(classes_call):
|
|
||||||
if isinstance(c, er.Array):
|
|
||||||
result += c.get_index_types()
|
|
||||||
else:
|
|
||||||
result.append(c)
|
|
||||||
for i, c in enumerate(result):
|
|
||||||
result[i] = er.Instance(c)
|
|
||||||
return result
|
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for c in evaluate.follow_call(classes[0]):
|
||||||
|
if isinstance(c, er.Array):
|
||||||
|
result += c.get_index_types()
|
||||||
|
else:
|
||||||
|
result.append(c)
|
||||||
|
for i, c in enumerate(result):
|
||||||
|
result[i] = er.Instance(c)
|
||||||
|
return result
|
||||||
|
|||||||
236
jedi/evaluate.py
236
jedi/evaluate.py
@@ -72,7 +72,6 @@ from _compatibility import next, hasattr, is_py3k, unicode, utf8
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import itertools
|
import itertools
|
||||||
import copy
|
|
||||||
|
|
||||||
import common
|
import common
|
||||||
import cache
|
import cache
|
||||||
@@ -82,7 +81,6 @@ import debug
|
|||||||
import builtin
|
import builtin
|
||||||
import imports
|
import imports
|
||||||
import recursion
|
import recursion
|
||||||
import helpers
|
|
||||||
import dynamic
|
import dynamic
|
||||||
import docstrings
|
import docstrings
|
||||||
|
|
||||||
@@ -249,12 +247,13 @@ def find_name(scope, name_str, position=None, search_global=False,
|
|||||||
def handle_for_loops(loop):
|
def handle_for_loops(loop):
|
||||||
# Take the first statement (for has always only
|
# Take the first statement (for has always only
|
||||||
# one, remember `in`). And follow it.
|
# one, remember `in`). And follow it.
|
||||||
if not len(loop.inits):
|
if not loop.inputs:
|
||||||
return []
|
return []
|
||||||
result = get_iterator_types(follow_statement(loop.inits[0]))
|
result = get_iterator_types(follow_statement(loop.inputs[0]))
|
||||||
if len(loop.set_vars) > 1:
|
if len(loop.set_vars) > 1:
|
||||||
var_arr = loop.set_stmt.get_assignment_calls()
|
commands = loop.set_stmt.get_commands()
|
||||||
result = assign_tuples(var_arr, result, name_str)
|
# loops with loop.set_vars > 0 only have one command
|
||||||
|
result = assign_tuples(commands[0], result, name_str)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def process(name):
|
def process(name):
|
||||||
@@ -286,22 +285,21 @@ def find_name(scope, name_str, position=None, search_global=False,
|
|||||||
inst.is_generated = True
|
inst.is_generated = True
|
||||||
result.append(inst)
|
result.append(inst)
|
||||||
elif par.isinstance(pr.Statement):
|
elif par.isinstance(pr.Statement):
|
||||||
def is_execution(arr):
|
def is_execution(calls):
|
||||||
for a in arr:
|
for c in calls:
|
||||||
a = a[0] # rest is always empty with assignees
|
if c.isinstance(pr.Array):
|
||||||
if a.isinstance(pr.Array):
|
if is_execution(c):
|
||||||
if is_execution(a):
|
|
||||||
return True
|
return True
|
||||||
elif a.isinstance(pr.Call):
|
elif c.isinstance(pr.Call):
|
||||||
# Compare start_pos, because names may be different
|
# Compare start_pos, because names may be different
|
||||||
# because of executions.
|
# because of executions.
|
||||||
if a.name.start_pos == name.start_pos \
|
if c.name.start_pos == name.start_pos \
|
||||||
and a.execution:
|
and c.execution:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
is_exe = False
|
is_exe = False
|
||||||
for op, assignee in par.assignment_details:
|
for assignee, op in par.assignment_details:
|
||||||
is_exe |= is_execution(assignee)
|
is_exe |= is_execution(assignee)
|
||||||
|
|
||||||
if is_exe:
|
if is_exe:
|
||||||
@@ -310,7 +308,7 @@ def find_name(scope, name_str, position=None, search_global=False,
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
details = par.assignment_details
|
details = par.assignment_details
|
||||||
if details and details[0][0] != '=':
|
if details and details[0][1] != '=':
|
||||||
no_break_scope = True
|
no_break_scope = True
|
||||||
|
|
||||||
# TODO this makes self variables non-breakable. wanted?
|
# TODO this makes self variables non-breakable. wanted?
|
||||||
@@ -374,7 +372,8 @@ def find_name(scope, name_str, position=None, search_global=False,
|
|||||||
if not result and isinstance(nscope, er.Instance):
|
if not result and isinstance(nscope, er.Instance):
|
||||||
# __getattr__ / __getattribute__
|
# __getattr__ / __getattribute__
|
||||||
result += check_getattr(nscope, name_str)
|
result += check_getattr(nscope, name_str)
|
||||||
debug.dbg('sfn filter "%s" in %s: %s' % (name_str, nscope, result))
|
debug.dbg('sfn filter "%s" in (%s-%s): %s@%s' % (name_str, scope,
|
||||||
|
nscope, result, position))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def descriptor_check(result):
|
def descriptor_check(result):
|
||||||
@@ -415,10 +414,10 @@ def check_getattr(inst, name_str):
|
|||||||
"""Checks for both __getattr__ and __getattribute__ methods"""
|
"""Checks for both __getattr__ and __getattribute__ methods"""
|
||||||
result = []
|
result = []
|
||||||
# str is important to lose the NamePart!
|
# str is important to lose the NamePart!
|
||||||
name = pr.Call(str(name_str), pr.Call.STRING, (0, 0), inst)
|
module = builtin.Builtin.scope
|
||||||
args = helpers.generate_param_array([name])
|
name = pr.Call(module, str(name_str), pr.Call.STRING, (0, 0), inst)
|
||||||
try:
|
try:
|
||||||
result = inst.execute_subscope_by_name('__getattr__', args)
|
result = inst.execute_subscope_by_name('__getattr__', [name])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
if not result:
|
if not result:
|
||||||
@@ -427,7 +426,7 @@ def check_getattr(inst, name_str):
|
|||||||
# 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:
|
try:
|
||||||
result = inst.execute_subscope_by_name('__getattribute__', args)
|
result = inst.execute_subscope_by_name('__getattribute__', [name])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return result
|
return result
|
||||||
@@ -485,46 +484,43 @@ def assign_tuples(tup, results, seek_name):
|
|||||||
def eval_results(index):
|
def eval_results(index):
|
||||||
types = []
|
types = []
|
||||||
for r in results:
|
for r in results:
|
||||||
if hasattr(r, "get_exact_index_types"):
|
try:
|
||||||
try:
|
func = r.get_exact_index_types
|
||||||
types += r.get_exact_index_types(index)
|
except AttributeError:
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
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:
|
||||||
|
try:
|
||||||
|
types += func(index)
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
return types
|
return types
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
if tup.type == pr.Array.NOARRAY:
|
for i, stmt in enumerate(tup):
|
||||||
# Here we have unnessecary braces, which we just remove.
|
# Used in assignments. There is just one call and no other things,
|
||||||
arr = tup.get_only_subelement()
|
# therefore we can just assume, that the first part is important.
|
||||||
if type(arr) == pr.Call:
|
command = stmt.get_commands()[0]
|
||||||
if arr.name.names[-1] == seek_name:
|
|
||||||
result = results
|
|
||||||
else:
|
|
||||||
result = assign_tuples(arr, results, seek_name)
|
|
||||||
else:
|
|
||||||
for i, t in enumerate(tup):
|
|
||||||
# Used in assignments. There is just one call and no other things,
|
|
||||||
# therefore we can just assume, that the first part is important.
|
|
||||||
if len(t) != 1:
|
|
||||||
raise AttributeError('Array length should be 1')
|
|
||||||
t = t[0]
|
|
||||||
|
|
||||||
# Check the left part, if there are still tuples in it or a Call.
|
if tup.type == pr.Array.NOARRAY:
|
||||||
if isinstance(t, pr.Array):
|
|
||||||
# These are "sub"-tuples.
|
# unnessecary braces -> just remove.
|
||||||
result += assign_tuples(t, eval_results(i), seek_name)
|
r = results
|
||||||
else:
|
else:
|
||||||
if t.name.names[-1] == seek_name:
|
r = eval_results(i)
|
||||||
result += eval_results(i)
|
|
||||||
|
# are there still tuples or is it just a Call.
|
||||||
|
if isinstance(command, pr.Array):
|
||||||
|
# These are "sub"-tuples.
|
||||||
|
result += assign_tuples(command, r, seek_name)
|
||||||
|
else:
|
||||||
|
if command.name.names[-1] == seek_name:
|
||||||
|
result += r
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@recursion.RecursionDecorator
|
@recursion.RecursionDecorator
|
||||||
@cache.memoize_default(default=[])
|
@cache.memoize_default(default=())
|
||||||
def follow_statement(stmt, seek_name=None):
|
def follow_statement(stmt, seek_name=None):
|
||||||
"""
|
"""
|
||||||
The starting point of the completion. A statement always owns a call list,
|
The starting point of the completion. A statement always owns a call list,
|
||||||
@@ -536,11 +532,11 @@ def follow_statement(stmt, seek_name=None):
|
|||||||
:param seek_name: A string.
|
:param seek_name: A string.
|
||||||
"""
|
"""
|
||||||
debug.dbg('follow_stmt %s (%s)' % (stmt, seek_name))
|
debug.dbg('follow_stmt %s (%s)' % (stmt, seek_name))
|
||||||
call_list = stmt.get_assignment_calls()
|
commands = stmt.get_commands()
|
||||||
debug.dbg('calls: %s' % call_list)
|
debug.dbg('calls: %s' % commands)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = follow_call_list(call_list)
|
result = follow_call_list(commands)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# This is so evil! But necessary to propagate errors. The attribute
|
# This is so evil! But necessary to propagate errors. The attribute
|
||||||
# errors here must not be catched, because they shouldn't exist.
|
# errors here must not be catched, because they shouldn't exist.
|
||||||
@@ -550,16 +546,15 @@ def follow_statement(stmt, seek_name=None):
|
|||||||
# variables.
|
# variables.
|
||||||
if len(stmt.get_set_vars()) > 1 and seek_name and stmt.assignment_details:
|
if len(stmt.get_set_vars()) > 1 and seek_name and stmt.assignment_details:
|
||||||
new_result = []
|
new_result = []
|
||||||
for op, set_vars in stmt.assignment_details:
|
for ass_commands, op in stmt.assignment_details:
|
||||||
new_result += assign_tuples(set_vars, result, seek_name)
|
new_result += assign_tuples(ass_commands[0], result, seek_name)
|
||||||
result = new_result
|
result = new_result
|
||||||
return set(result)
|
return set(result)
|
||||||
|
|
||||||
|
|
||||||
def follow_call_list(call_list, follow_array=False):
|
def follow_call_list(call_list, follow_array=False):
|
||||||
"""
|
"""
|
||||||
The call_list has a special structure.
|
`call_list` can be either `pr.Array` or `list of list`.
|
||||||
This can be either `pr.Array` or `list of list`.
|
|
||||||
It is used to evaluate a two dimensional object, that has calls, arrays and
|
It is used to evaluate a two dimensional object, that has calls, arrays and
|
||||||
operators in it.
|
operators in it.
|
||||||
"""
|
"""
|
||||||
@@ -570,79 +565,72 @@ def follow_call_list(call_list, follow_array=False):
|
|||||||
# is nested LC
|
# is nested LC
|
||||||
input = nested_lc.stmt
|
input = nested_lc.stmt
|
||||||
module = input.get_parent_until()
|
module = input.get_parent_until()
|
||||||
loop = pr.ForFlow(module, [input], lc.stmt.start_pos,
|
# create a for loop, which does the same as list comprehensions
|
||||||
lc.middle, True)
|
loop = pr.ForFlow(module, [input], lc.stmt.start_pos, lc.middle, True)
|
||||||
|
|
||||||
loop.parent = lc.stmt.parent if parent is None else parent
|
loop.parent = parent or lc.get_parent_until(pr.IsScope)
|
||||||
|
|
||||||
if isinstance(nested_lc, pr.ListComprehension):
|
if isinstance(nested_lc, pr.ListComprehension):
|
||||||
loop = evaluate_list_comprehension(nested_lc, loop)
|
loop = evaluate_list_comprehension(nested_lc, loop)
|
||||||
return loop
|
return loop
|
||||||
|
|
||||||
if pr.Array.is_type(call_list, pr.Array.TUPLE, pr.Array.DICT):
|
result = []
|
||||||
# Tuples can stand just alone without any braces. These would be
|
calls_iterator = iter(call_list)
|
||||||
# recognized as separate calls, but actually are a tuple.
|
for call in calls_iterator:
|
||||||
result = follow_call(call_list)
|
if pr.Array.is_type(call, pr.Array.NOARRAY):
|
||||||
else:
|
r = list(itertools.chain.from_iterable(follow_statement(s)
|
||||||
result = []
|
for s in call))
|
||||||
for calls in call_list:
|
call_path = call.generate_call_path()
|
||||||
calls_iterator = iter(calls)
|
next(call_path, None) # the first one has been used already
|
||||||
for call in calls_iterator:
|
result += follow_paths(call_path, r, call.parent,
|
||||||
if pr.Array.is_type(call, pr.Array.NOARRAY):
|
position=call.start_pos)
|
||||||
result += follow_call_list(call, follow_array=True)
|
elif isinstance(call, pr.ListComprehension):
|
||||||
elif isinstance(call, pr.ListComprehension):
|
loop = evaluate_list_comprehension(call)
|
||||||
loop = evaluate_list_comprehension(call)
|
# Caveat: parents are being changed, but this doesn't matter,
|
||||||
stmt = copy.copy(call.stmt)
|
# because nothing else uses it.
|
||||||
stmt.parent = loop
|
call.stmt.parent = loop
|
||||||
# create a for loop which does the same as list
|
result += follow_statement(call.stmt)
|
||||||
# comprehensions
|
else:
|
||||||
result += follow_statement(stmt)
|
if isinstance(call, pr.Lambda):
|
||||||
else:
|
result.append(er.Function(call))
|
||||||
if isinstance(call, (pr.Lambda)):
|
# With things like params, these can also be functions...
|
||||||
result.append(er.Function(call))
|
elif isinstance(call, (er.Function, er.Class, er.Instance,
|
||||||
# With things like params, these can also be functions...
|
dynamic.ArrayInstance)):
|
||||||
elif isinstance(call, (er.Function, er.Class, er.Instance,
|
result.append(call)
|
||||||
dynamic.ArrayInstance)):
|
# The string tokens are just operations (+, -, etc.)
|
||||||
result.append(call)
|
elif not isinstance(call, (str, unicode)):
|
||||||
# The string tokens are just operations (+, -, etc.)
|
if str(call.name) == 'if':
|
||||||
elif not isinstance(call, (str, unicode)):
|
# Ternary operators.
|
||||||
if str(call.name) == 'if':
|
while True:
|
||||||
# Ternary operators.
|
try:
|
||||||
while True:
|
call = next(calls_iterator)
|
||||||
try:
|
except StopIteration:
|
||||||
call = next(calls_iterator)
|
break
|
||||||
except StopIteration:
|
try:
|
||||||
break
|
if str(call.name) == 'else':
|
||||||
try:
|
break
|
||||||
if str(call.name) == 'else':
|
except AttributeError:
|
||||||
break
|
pass
|
||||||
except AttributeError:
|
continue
|
||||||
pass
|
result += follow_call(call)
|
||||||
continue
|
elif call == '*':
|
||||||
result += follow_call(call)
|
if [r for r in result if isinstance(r, er.Array)
|
||||||
elif call == '*':
|
or isinstance(r, er.Instance)
|
||||||
if [r for r in result if isinstance(r, er.Array)
|
and str(r.name) == 'str']:
|
||||||
or isinstance(r, er.Instance)
|
# if it is an iterable, ignore * operations
|
||||||
and str(r.name) == 'str']:
|
next(calls_iterator)
|
||||||
# if it is an iterable, ignore * operations
|
|
||||||
next(calls_iterator)
|
|
||||||
|
|
||||||
if follow_array and isinstance(call_list, pr.Array):
|
|
||||||
# call_list can also be a two dimensional array
|
|
||||||
call_path = call_list.generate_call_path()
|
|
||||||
next(call_path, None) # the first one has been used already
|
|
||||||
call_scope = call_list.parent_stmt
|
|
||||||
position = call_list.start_pos
|
|
||||||
result = follow_paths(call_path, result, call_scope, position=position)
|
|
||||||
return set(result)
|
return set(result)
|
||||||
|
|
||||||
|
|
||||||
def follow_call(call):
|
def follow_call(call):
|
||||||
"""Follow a call is following a function, variable, string, etc."""
|
"""Follow a call is following a function, variable, string, etc."""
|
||||||
scope = call.parent_stmt.parent
|
|
||||||
path = call.generate_call_path()
|
path = call.generate_call_path()
|
||||||
position = call.parent_stmt.start_pos
|
|
||||||
return follow_call_path(path, scope, position)
|
# find the statement of the Scope
|
||||||
|
s = call
|
||||||
|
while not s.parent.isinstance(pr.IsScope):
|
||||||
|
s = s.parent
|
||||||
|
return follow_call_path(path, s.parent, s.start_pos)
|
||||||
|
|
||||||
|
|
||||||
def follow_call_path(path, scope, position):
|
def follow_call_path(path, scope, position):
|
||||||
@@ -664,8 +652,7 @@ def follow_call_path(path, scope, position):
|
|||||||
debug.warning('unknown type:', current.type, current)
|
debug.warning('unknown type:', current.type, current)
|
||||||
scopes = []
|
scopes = []
|
||||||
# Make instances of those number/string objects.
|
# Make instances of those number/string objects.
|
||||||
arr = helpers.generate_param_array([current.name])
|
scopes = [er.Instance(s, (current.name,)) for s in scopes]
|
||||||
scopes = [er.Instance(s, arr) for s in scopes]
|
|
||||||
result = imports.strip_imports(scopes)
|
result = imports.strip_imports(scopes)
|
||||||
|
|
||||||
return follow_paths(path, result, scope, position=position)
|
return follow_paths(path, result, scope, position=position)
|
||||||
@@ -745,11 +732,12 @@ def filter_private_variable(scope, call_scope, var_name):
|
|||||||
|
|
||||||
def goto(stmt, call_path=None):
|
def goto(stmt, call_path=None):
|
||||||
if call_path is None:
|
if call_path is None:
|
||||||
arr = stmt.get_assignment_calls()
|
commands = stmt.get_commands()
|
||||||
call = arr.get_only_subelement()
|
assert len(commands) == 1
|
||||||
|
call = commands[0]
|
||||||
call_path = list(call.generate_call_path())
|
call_path = list(call.generate_call_path())
|
||||||
|
|
||||||
scope = stmt.parent
|
scope = stmt.get_parent_until(pr.IsScope)
|
||||||
pos = stmt.start_pos
|
pos = stmt.start_pos
|
||||||
call_path, search = call_path[:-1], call_path[-1]
|
call_path, search = call_path[:-1], call_path[-1]
|
||||||
pos = pos[0], pos[1] + 1
|
pos = pos[0], pos[1] + 1
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ they change classes in Python 3.
|
|||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
import copy
|
import copy
|
||||||
|
import itertools
|
||||||
|
|
||||||
from _compatibility import property, use_metaclass, next, hasattr
|
from _compatibility import property, use_metaclass, next, hasattr
|
||||||
import parsing_representation as pr
|
import parsing_representation as pr
|
||||||
@@ -33,13 +34,13 @@ class DecoratorNotFound(LookupError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Executable(pr.Base):
|
class Executable(pr.IsScope):
|
||||||
""" An instance is also an executable - because __init__ is called """
|
"""
|
||||||
def __init__(self, base, var_args=None):
|
An instance is also an executable - because __init__ is called
|
||||||
|
:param var_args: The param input array, consist of `pr.Array` or list.
|
||||||
|
"""
|
||||||
|
def __init__(self, base, var_args=()):
|
||||||
self.base = base
|
self.base = base
|
||||||
# The param input array.
|
|
||||||
if var_args is None:
|
|
||||||
var_args = pr.Array(None, None)
|
|
||||||
self.var_args = var_args
|
self.var_args = var_args
|
||||||
|
|
||||||
def get_parent_until(self, *args, **kwargs):
|
def get_parent_until(self, *args, **kwargs):
|
||||||
@@ -52,7 +53,7 @@ class Executable(pr.Base):
|
|||||||
|
|
||||||
class Instance(use_metaclass(cache.CachedMetaClass, Executable)):
|
class Instance(use_metaclass(cache.CachedMetaClass, Executable)):
|
||||||
""" This class is used to evaluate instances. """
|
""" This class is used to evaluate instances. """
|
||||||
def __init__(self, base, var_args=None):
|
def __init__(self, base, var_args=()):
|
||||||
super(Instance, self).__init__(base, var_args)
|
super(Instance, self).__init__(base, var_args)
|
||||||
if str(base.name) in ['list', 'set'] \
|
if str(base.name) in ['list', 'set'] \
|
||||||
and builtin.Builtin.scope == base.get_parent_until():
|
and builtin.Builtin.scope == base.get_parent_until():
|
||||||
@@ -121,20 +122,15 @@ class Instance(use_metaclass(cache.CachedMetaClass, Executable)):
|
|||||||
sub = self.base.get_subscope_by_name(name)
|
sub = self.base.get_subscope_by_name(name)
|
||||||
return InstanceElement(self, sub, True)
|
return InstanceElement(self, sub, True)
|
||||||
|
|
||||||
def execute_subscope_by_name(self, name, args=None):
|
def execute_subscope_by_name(self, name, args=()):
|
||||||
if args is None:
|
|
||||||
args = helpers.generate_param_array([])
|
|
||||||
method = self.get_subscope_by_name(name)
|
method = self.get_subscope_by_name(name)
|
||||||
if args.parent_stmt is None:
|
|
||||||
args.parent_stmt = method
|
|
||||||
return Execution(method, args).get_return_types()
|
return Execution(method, args).get_return_types()
|
||||||
|
|
||||||
def get_descriptor_return(self, obj):
|
def get_descriptor_return(self, obj):
|
||||||
""" Throws a KeyError if there's no method. """
|
""" Throws a KeyError if there's no method. """
|
||||||
# Arguments in __get__ descriptors are obj, class.
|
# Arguments in __get__ descriptors are obj, class.
|
||||||
# `method` is the new parent of the array, don't know if that's good.
|
# `method` is the new parent of the array, don't know if that's good.
|
||||||
v = [obj, obj.base] if isinstance(obj, Instance) else [None, obj]
|
args = [obj, obj.base] if isinstance(obj, Instance) else [None, obj]
|
||||||
args = helpers.generate_param_array(v)
|
|
||||||
return self.execute_subscope_by_name('__get__', args)
|
return self.execute_subscope_by_name('__get__', args)
|
||||||
|
|
||||||
@cache.memoize_default([])
|
@cache.memoize_default([])
|
||||||
@@ -164,7 +160,7 @@ class Instance(use_metaclass(cache.CachedMetaClass, Executable)):
|
|||||||
yield self, names
|
yield self, names
|
||||||
|
|
||||||
def get_index_types(self, index=None):
|
def get_index_types(self, index=None):
|
||||||
args = helpers.generate_param_array([] if index is None else [index])
|
args = [] if index is None else [index]
|
||||||
try:
|
try:
|
||||||
return self.execute_subscope_by_name('__getitem__', args)
|
return self.execute_subscope_by_name('__getitem__', args)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -219,15 +215,10 @@ class InstanceElement(use_metaclass(cache.CachedMetaClass)):
|
|||||||
return self
|
return self
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def get_assignment_calls(self):
|
def get_commands(self):
|
||||||
# Copy and modify the array.
|
# Copy and modify the array.
|
||||||
origin = self.var.get_assignment_calls()
|
return [InstanceElement(self.instance, command, self.is_class_var)
|
||||||
# Delete parent, because it isn't used anymore.
|
for command in self.var.get_commands()]
|
||||||
new = helpers.fast_parent_copy(origin)
|
|
||||||
par = InstanceElement(self.instance, origin.parent_stmt,
|
|
||||||
self.is_class_var)
|
|
||||||
new.parent_stmt = par
|
|
||||||
return new
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(self.var, name)
|
return getattr(self.var, name)
|
||||||
@@ -239,7 +230,7 @@ class InstanceElement(use_metaclass(cache.CachedMetaClass)):
|
|||||||
return "<%s of %s>" % (type(self).__name__, self.var)
|
return "<%s of %s>" % (type(self).__name__, self.var)
|
||||||
|
|
||||||
|
|
||||||
class Class(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
class Class(use_metaclass(cache.CachedMetaClass, pr.IsScope)):
|
||||||
"""
|
"""
|
||||||
This class is not only important to extend `pr.Class`, it is also a
|
This class is not only important to extend `pr.Class`, it is also a
|
||||||
important for descriptors (if the descriptor methods are evaluated or not).
|
important for descriptors (if the descriptor methods are evaluated or not).
|
||||||
@@ -247,7 +238,7 @@ class Class(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
|||||||
def __init__(self, base):
|
def __init__(self, base):
|
||||||
self.base = base
|
self.base = base
|
||||||
|
|
||||||
@cache.memoize_default(default=[])
|
@cache.memoize_default(default=())
|
||||||
def get_super_classes(self):
|
def get_super_classes(self):
|
||||||
supers = []
|
supers = []
|
||||||
# TODO care for mro stuff (multiple super classes).
|
# TODO care for mro stuff (multiple super classes).
|
||||||
@@ -263,7 +254,7 @@ class Class(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
|||||||
supers += evaluate.find_name(builtin.Builtin.scope, 'object')
|
supers += evaluate.find_name(builtin.Builtin.scope, 'object')
|
||||||
return supers
|
return supers
|
||||||
|
|
||||||
@cache.memoize_default(default=[])
|
@cache.memoize_default(default=())
|
||||||
def get_defined_names(self):
|
def get_defined_names(self):
|
||||||
def in_iterable(name, iterable):
|
def in_iterable(name, iterable):
|
||||||
""" checks if the name is in the variable 'iterable'. """
|
""" checks if the name is in the variable 'iterable'. """
|
||||||
@@ -296,20 +287,19 @@ class Class(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
|||||||
return self.base.name
|
return self.base.name
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name not in ['start_pos', 'end_pos', 'parent', 'subscopes',
|
if name not in ['start_pos', 'end_pos', 'parent', 'asserts', 'docstr',
|
||||||
'get_imports', 'get_parent_until', 'docstr', 'asserts']:
|
'get_imports', 'get_parent_until', 'get_code', 'subscopes']:
|
||||||
raise AttributeError("Don't touch this (%s)!" % name)
|
raise AttributeError("Don't touch this: %s of %s !" % (name, self))
|
||||||
return getattr(self.base, name)
|
return getattr(self.base, name)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<e%s of %s>" % (type(self).__name__, self.base)
|
return "<e%s of %s>" % (type(self).__name__, self.base)
|
||||||
|
|
||||||
|
|
||||||
class Function(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
class Function(use_metaclass(cache.CachedMetaClass, pr.IsScope)):
|
||||||
"""
|
"""
|
||||||
Needed because of decorators. Decorators are evaluated here.
|
Needed because of decorators. Decorators are evaluated here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, func, is_decorated=False):
|
def __init__(self, func, is_decorated=False):
|
||||||
""" This should not be called directly """
|
""" This should not be called directly """
|
||||||
self.base_func = func
|
self.base_func = func
|
||||||
@@ -339,9 +329,8 @@ class Function(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
|||||||
decorator = dec_results.pop()
|
decorator = dec_results.pop()
|
||||||
# Create param array.
|
# Create param array.
|
||||||
old_func = Function(f, is_decorated=True)
|
old_func = Function(f, is_decorated=True)
|
||||||
params = helpers.generate_param_array([old_func], old_func)
|
|
||||||
|
|
||||||
wrappers = Execution(decorator, params).get_return_types()
|
wrappers = Execution(decorator, (old_func,)).get_return_types()
|
||||||
if not len(wrappers):
|
if not len(wrappers):
|
||||||
debug.warning('no wrappers found', self.base_func)
|
debug.warning('no wrappers found', self.base_func)
|
||||||
return None
|
return None
|
||||||
@@ -388,7 +377,18 @@ class Execution(Executable):
|
|||||||
multiple calls to functions and recursion has to be avoided. But this is
|
multiple calls to functions and recursion has to be avoided. But this is
|
||||||
responsibility of the decorators.
|
responsibility of the decorators.
|
||||||
"""
|
"""
|
||||||
@cache.memoize_default(default=[])
|
def follow_var_arg(self, index):
|
||||||
|
try:
|
||||||
|
stmt = self.var_args[index]
|
||||||
|
except IndexError:
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
if isinstance(stmt, pr.Statement):
|
||||||
|
return evaluate.follow_statement(stmt)
|
||||||
|
else:
|
||||||
|
return [stmt] # just some arbitrary object
|
||||||
|
|
||||||
|
@cache.memoize_default(default=())
|
||||||
@recursion.ExecutionRecursionDecorator
|
@recursion.ExecutionRecursionDecorator
|
||||||
def get_return_types(self, evaluate_generator=False):
|
def get_return_types(self, evaluate_generator=False):
|
||||||
""" Get the return types of a function. """
|
""" Get the return types of a function. """
|
||||||
@@ -401,8 +401,8 @@ class Execution(Executable):
|
|||||||
if func_name == 'getattr':
|
if func_name == 'getattr':
|
||||||
# follow the first param
|
# follow the first param
|
||||||
try:
|
try:
|
||||||
objects = evaluate.follow_call_list([self.var_args[0]])
|
objects = self.follow_var_arg(0)
|
||||||
names = evaluate.follow_call_list([self.var_args[1]])
|
names = self.follow_var_arg(1)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
debug.warning('getattr() called with to few args.')
|
debug.warning('getattr() called with to few args.')
|
||||||
return []
|
return []
|
||||||
@@ -412,19 +412,22 @@ class Execution(Executable):
|
|||||||
debug.warning('getattr called without instance')
|
debug.warning('getattr called without instance')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for name in names:
|
for arr_name in names:
|
||||||
key = name.var_args.get_only_subelement()
|
if len(arr_name.var_args) != 1:
|
||||||
|
debug.warning('jedi getattr is too simple')
|
||||||
|
key = arr_name.var_args[0]
|
||||||
stmts += evaluate.follow_path(iter([key]), obj,
|
stmts += evaluate.follow_path(iter([key]), obj,
|
||||||
self.base)
|
self.base)
|
||||||
return stmts
|
return stmts
|
||||||
elif func_name == 'type':
|
elif func_name == 'type':
|
||||||
# otherwise it would be a metaclass
|
# otherwise it would be a metaclass
|
||||||
if len(self.var_args) == 1:
|
if len(self.var_args) == 1:
|
||||||
objects = evaluate.follow_call_list([self.var_args[0]])
|
objects = self.follow_var_arg(0)
|
||||||
return [o.base for o in objects if isinstance(o, Instance)]
|
return [o.base for o in objects if isinstance(o, Instance)]
|
||||||
elif func_name == 'super':
|
elif func_name == 'super':
|
||||||
|
# TODO make this able to detect multiple inheritance supers
|
||||||
accept = (pr.Function,)
|
accept = (pr.Function,)
|
||||||
func = self.var_args.parent_stmt.get_parent_until(accept)
|
func = self.var_args.get_parent_until(accept)
|
||||||
if func.isinstance(*accept):
|
if func.isinstance(*accept):
|
||||||
cls = func.get_parent_until(accept + (pr.Class,),
|
cls = func.get_parent_until(accept + (pr.Class,),
|
||||||
include_current=False)
|
include_current=False)
|
||||||
@@ -476,7 +479,7 @@ class Execution(Executable):
|
|||||||
stmts += evaluate.follow_statement(r)
|
stmts += evaluate.follow_statement(r)
|
||||||
return stmts
|
return stmts
|
||||||
|
|
||||||
@cache.memoize_default(default=[])
|
@cache.memoize_default(default=())
|
||||||
def get_params(self):
|
def get_params(self):
|
||||||
"""
|
"""
|
||||||
This returns the params for an Execution/Instance and is injected as a
|
This returns the params for an Execution/Instance and is injected as a
|
||||||
@@ -484,22 +487,36 @@ class Execution(Executable):
|
|||||||
This needs to be here, because Instance can have __init__ functions,
|
This needs to be here, because Instance can have __init__ functions,
|
||||||
which act the same way as normal functions.
|
which act the same way as normal functions.
|
||||||
"""
|
"""
|
||||||
def gen_param_name_copy(param, keys=[], values=[], array_type=None):
|
def gen_param_name_copy(param, keys=(), values=(), array_type=None):
|
||||||
"""
|
"""
|
||||||
Create a param with the original scope (of varargs) as parent.
|
Create a param with the original scope (of varargs) as parent.
|
||||||
"""
|
"""
|
||||||
parent_stmt = self.var_args.parent_stmt
|
if isinstance(self.var_args, pr.Array):
|
||||||
pos = parent_stmt.start_pos if parent_stmt else None
|
parent = self.var_args.parent
|
||||||
calls = pr.Array(pos, pr.Array.NOARRAY, parent_stmt)
|
start_pos = self.var_args.start_pos
|
||||||
calls.values = values
|
else:
|
||||||
calls.keys = keys
|
parent = self.base
|
||||||
calls.type = array_type
|
start_pos = 0, 0
|
||||||
|
|
||||||
new_param = copy.copy(param)
|
new_param = copy.copy(param)
|
||||||
if parent_stmt is not None:
|
|
||||||
new_param.parent = parent_stmt
|
|
||||||
new_param._assignment_calls_calculated = True
|
|
||||||
new_param._assignment_calls = calls
|
|
||||||
new_param.is_generated = True
|
new_param.is_generated = True
|
||||||
|
if parent is not None:
|
||||||
|
new_param.parent = parent
|
||||||
|
|
||||||
|
# create an Array (-> needed for *args/**kwargs tuples/dicts)
|
||||||
|
arr = pr.Array(self._sub_module, start_pos, array_type, parent)
|
||||||
|
arr.values = values
|
||||||
|
key_stmts = []
|
||||||
|
for key in keys:
|
||||||
|
stmt = pr.Statement(self._sub_module, [], [], [],
|
||||||
|
start_pos, None)
|
||||||
|
stmt._commands = [key]
|
||||||
|
key_stmts.append(stmt)
|
||||||
|
arr.keys = key_stmts
|
||||||
|
arr.type = array_type
|
||||||
|
|
||||||
|
new_param._commands = [arr]
|
||||||
|
|
||||||
name = copy.copy(param.get_name())
|
name = copy.copy(param.get_name())
|
||||||
name.parent = new_param
|
name.parent = new_param
|
||||||
return name
|
return name
|
||||||
@@ -542,12 +559,12 @@ class Execution(Executable):
|
|||||||
values=[value]))
|
values=[value]))
|
||||||
key, value = next(var_arg_iterator, (None, None))
|
key, value = next(var_arg_iterator, (None, None))
|
||||||
|
|
||||||
assignments = param.get_assignment_calls().values
|
commands = param.get_commands()
|
||||||
assignment = assignments[0]
|
|
||||||
keys = []
|
keys = []
|
||||||
values = []
|
values = []
|
||||||
array_type = None
|
array_type = None
|
||||||
if assignment[0] == '*':
|
ignore_creation = False
|
||||||
|
if commands[0] == '*':
|
||||||
# *args param
|
# *args param
|
||||||
array_type = pr.Array.TUPLE
|
array_type = pr.Array.TUPLE
|
||||||
if value:
|
if value:
|
||||||
@@ -558,19 +575,21 @@ class Execution(Executable):
|
|||||||
var_arg_iterator.push_back((key, value))
|
var_arg_iterator.push_back((key, value))
|
||||||
break
|
break
|
||||||
values.append(value)
|
values.append(value)
|
||||||
elif assignment[0] == '**':
|
elif commands[0] == '**':
|
||||||
# **kwargs param
|
# **kwargs param
|
||||||
array_type = pr.Array.DICT
|
array_type = pr.Array.DICT
|
||||||
if non_matching_keys:
|
if non_matching_keys:
|
||||||
keys, values = zip(*non_matching_keys)
|
keys, values = zip(*non_matching_keys)
|
||||||
else:
|
elif not keys_only:
|
||||||
# normal param
|
# normal param
|
||||||
if value:
|
if value is not None:
|
||||||
values = [value]
|
values = [value]
|
||||||
else:
|
else:
|
||||||
if param.assignment_details:
|
if param.assignment_details:
|
||||||
# No value: return the default values.
|
# No value: return the default values.
|
||||||
values = assignments
|
ignore_creation = True
|
||||||
|
result.append(param.get_name())
|
||||||
|
param.is_generated = True
|
||||||
else:
|
else:
|
||||||
# If there is no assignment detail, that means there is
|
# If there is no assignment detail, that means there is
|
||||||
# no assignment, just the result. Therefore nothing has
|
# no assignment, just the result. Therefore nothing has
|
||||||
@@ -579,7 +598,7 @@ class Execution(Executable):
|
|||||||
|
|
||||||
# Just ignore all the params that are without a key, after one
|
# Just ignore all the params that are without a key, after one
|
||||||
# keyword argument was set.
|
# keyword argument was set.
|
||||||
if not keys_only or assignment[0] == '**':
|
if not ignore_creation and (not keys_only or commands[0] == '**'):
|
||||||
keys_used.add(str(key))
|
keys_used.add(str(key))
|
||||||
result.append(gen_param_name_copy(param, keys=keys,
|
result.append(gen_param_name_copy(param, keys=keys,
|
||||||
values=values, array_type=array_type))
|
values=values, array_type=array_type))
|
||||||
@@ -597,37 +616,44 @@ class Execution(Executable):
|
|||||||
"""
|
"""
|
||||||
def iterate():
|
def iterate():
|
||||||
# `var_args` is typically an Array, and not a list.
|
# `var_args` is typically an Array, and not a list.
|
||||||
for var_arg in self.var_args:
|
for stmt in self.var_args:
|
||||||
# empty var_arg
|
if not isinstance(stmt, pr.Statement):
|
||||||
if len(var_arg) == 0:
|
if stmt is None:
|
||||||
yield None, None
|
yield None, None
|
||||||
|
continue
|
||||||
|
old = stmt
|
||||||
|
# generate a statement if it's not already one.
|
||||||
|
module = builtin.Builtin.scope
|
||||||
|
stmt = pr.Statement(module, [], [], [], (0, 0), None)
|
||||||
|
stmt._commands = [old]
|
||||||
|
|
||||||
# *args
|
# *args
|
||||||
elif var_arg[0] == '*':
|
if stmt.get_commands()[0] == '*':
|
||||||
arrays = evaluate.follow_call_list([var_arg[1:]])
|
arrays = evaluate.follow_call_list(stmt.get_commands()[1:])
|
||||||
|
# *args must be some sort of an array, otherwise -> ignore
|
||||||
for array in arrays:
|
for array in arrays:
|
||||||
if hasattr(array, 'get_contents'):
|
for field_stmt in array: # yield from plz!
|
||||||
for field in array.get_contents():
|
yield None, field_stmt
|
||||||
yield None, field
|
|
||||||
# **kwargs
|
# **kwargs
|
||||||
elif var_arg[0] == '**':
|
elif stmt.get_commands()[0] == '**':
|
||||||
arrays = evaluate.follow_call_list([var_arg[1:]])
|
arrays = evaluate.follow_call_list(stmt.get_commands()[1:])
|
||||||
for array in arrays:
|
for array in arrays:
|
||||||
if hasattr(array, 'get_contents'):
|
for key_stmt, value_stmt in array.items():
|
||||||
for key, field in array.get_contents():
|
# first index, is the key if syntactically correct
|
||||||
# Take the first index.
|
call = key_stmt.get_commands()[0]
|
||||||
if isinstance(key, pr.Name):
|
if isinstance(call, pr.Name):
|
||||||
name = key
|
yield call, value_stmt
|
||||||
else:
|
elif type(call) == pr.Call:
|
||||||
# `pr`.[Call|Function|Class] lookup.
|
yield call.name, value_stmt
|
||||||
name = key[0].name
|
|
||||||
yield name, field
|
|
||||||
# Normal arguments (including key arguments).
|
# Normal arguments (including key arguments).
|
||||||
else:
|
else:
|
||||||
if len(var_arg) > 1 and var_arg[1] == '=':
|
if stmt.assignment_details:
|
||||||
# This is a named parameter (var_arg[0] is a Call).
|
key_arr, op = stmt.assignment_details[0]
|
||||||
yield var_arg[0].name, var_arg[2:]
|
# named parameter
|
||||||
|
if key_arr and isinstance(key_arr[0], pr.Call):
|
||||||
|
yield key_arr[0].name, stmt
|
||||||
else:
|
else:
|
||||||
yield None, var_arg
|
yield None, stmt
|
||||||
|
|
||||||
return iter(common.PushBackIterator(iterate()))
|
return iter(common.PushBackIterator(iterate()))
|
||||||
|
|
||||||
@@ -666,7 +692,7 @@ class Execution(Executable):
|
|||||||
raise common.MultiLevelAttributeError(sys.exc_info())
|
raise common.MultiLevelAttributeError(sys.exc_info())
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name not in ['start_pos', 'end_pos', 'imports']:
|
if name not in ['start_pos', 'end_pos', 'imports', '_sub_module']:
|
||||||
raise AttributeError('Tried to access %s: %s. Why?' % (name, self))
|
raise AttributeError('Tried to access %s: %s. Why?' % (name, self))
|
||||||
return getattr(self.base, name)
|
return getattr(self.base, name)
|
||||||
|
|
||||||
@@ -763,60 +789,60 @@ class Array(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
|||||||
def __init__(self, array):
|
def __init__(self, array):
|
||||||
self._array = array
|
self._array = array
|
||||||
|
|
||||||
def get_index_types(self, index_call_list=None):
|
def get_index_types(self, index_arr=None):
|
||||||
""" Get the types of a specific index or all, if not given """
|
""" Get the types of a specific index or all, if not given """
|
||||||
# array slicing
|
if index_arr is not None:
|
||||||
if index_call_list is not None:
|
if index_arr and [x for x in index_arr if ':' in x.get_commands()]:
|
||||||
if index_call_list and [x for x in index_call_list if ':' in x]:
|
# array slicing
|
||||||
return [self]
|
return [self]
|
||||||
|
|
||||||
index_possibilities = list(evaluate.follow_call_list(
|
index_possibilities = self._follow_values(index_arr)
|
||||||
index_call_list))
|
|
||||||
if len(index_possibilities) == 1:
|
if len(index_possibilities) == 1:
|
||||||
# This is indexing only one element, with a fixed index number,
|
# This is indexing only one element, with a fixed index number,
|
||||||
# otherwise it just ignores the index (e.g. [1+1]).
|
# otherwise it just ignores the index (e.g. [1+1]).
|
||||||
try:
|
index = index_possibilities[0]
|
||||||
# Multiple elements in the array are not wanted. var_args
|
if isinstance(index, Instance) \
|
||||||
# and get_only_subelement can raise AttributeErrors.
|
and str(index.name) in ['int', 'str'] \
|
||||||
i = index_possibilities[0].var_args.get_only_subelement()
|
and len(index.var_args) == 1:
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
return self.get_exact_index_types(i)
|
return self.get_exact_index_types(index.var_args[0])
|
||||||
except (IndexError, KeyError):
|
except (KeyError, IndexError):
|
||||||
pass
|
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)
|
||||||
return set(result)
|
return set(result)
|
||||||
|
|
||||||
def get_exact_index_types(self, index):
|
def get_exact_index_types(self, mixed_index):
|
||||||
""" Here the index is an int. Raises IndexError/KeyError """
|
""" Here the index is an int/str. Raises IndexError/KeyError """
|
||||||
if self._array.type == pr.Array.DICT:
|
index = mixed_index
|
||||||
old_index = index
|
if self.type == pr.Array.DICT:
|
||||||
index = None
|
index = None
|
||||||
for i, key_elements in enumerate(self._array.keys):
|
for i, key_statement in enumerate(self._array.keys):
|
||||||
# Because we only want the key to be a string.
|
# Because we only want the key to be a string.
|
||||||
if len(key_elements) == 1:
|
key_commands = key_statement.get_commands()
|
||||||
try:
|
if len(key_commands) != 1: # cannot deal with complex strings
|
||||||
str_key = key_elements.get_code()
|
continue
|
||||||
except AttributeError:
|
key = key_commands[0]
|
||||||
try:
|
if isinstance(key, pr.Call) and key.type == pr.Call.STRING:
|
||||||
str_key = key_elements[0].name
|
str_key = key.name
|
||||||
except AttributeError:
|
elif isinstance(key, pr.Name):
|
||||||
str_key = None
|
str_key = str(key)
|
||||||
if old_index == str_key:
|
|
||||||
index = i
|
if mixed_index == str_key:
|
||||||
break
|
index = i
|
||||||
|
break
|
||||||
if index is None:
|
if index is None:
|
||||||
raise KeyError('No key found in dictionary')
|
raise KeyError('No key found in dictionary')
|
||||||
values = [self._array[index]]
|
|
||||||
return self.follow_values(values)
|
|
||||||
|
|
||||||
def follow_values(self, values):
|
# Can raise an IndexError
|
||||||
|
values = [self._array.values[index]]
|
||||||
|
return self._follow_values(values)
|
||||||
|
|
||||||
|
def _follow_values(self, values):
|
||||||
""" helper function for the index getters """
|
""" helper function for the index getters """
|
||||||
return evaluate.follow_call_list(values)
|
return list(itertools.chain.from_iterable(evaluate.follow_statement(v)
|
||||||
|
for v in values))
|
||||||
|
|
||||||
def get_defined_names(self):
|
def get_defined_names(self):
|
||||||
"""
|
"""
|
||||||
@@ -829,24 +855,28 @@ class Array(use_metaclass(cache.CachedMetaClass, pr.Base)):
|
|||||||
names = scope.get_defined_names()
|
names = scope.get_defined_names()
|
||||||
return [ArrayMethod(n) for n in names]
|
return [ArrayMethod(n) for n in names]
|
||||||
|
|
||||||
def get_contents(self):
|
|
||||||
return self._array
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parent(self):
|
def parent(self):
|
||||||
"""
|
|
||||||
Return the builtin scope as parent, because the arrays are builtins
|
|
||||||
"""
|
|
||||||
return builtin.Builtin.scope
|
return builtin.Builtin.scope
|
||||||
|
|
||||||
def get_parent_until(self, *args, **kwargs):
|
def get_parent_until(self):
|
||||||
return builtin.Builtin.scope
|
return builtin.Builtin.scope
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name not in ['type', 'start_pos', 'get_only_subelement']:
|
if name not in ['type', 'start_pos', 'get_only_subelement', 'parent',
|
||||||
|
'get_parent_until', 'items']:
|
||||||
raise AttributeError('Strange access on %s: %s.' % (self, name))
|
raise AttributeError('Strange access on %s: %s.' % (self, name))
|
||||||
return getattr(self._array, name)
|
return getattr(self._array, name)
|
||||||
|
|
||||||
|
def __getitem__(self):
|
||||||
|
return self._array.__getitem__()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self._array.__iter__()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self._array.__len__()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<e%s of %s>" % (type(self).__name__, self._array)
|
return "<e%s of %s>" % (type(self).__name__, self._array)
|
||||||
|
|
||||||
@@ -863,7 +893,7 @@ class ArrayMethod(object):
|
|||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
# Set access privileges:
|
# Set access privileges:
|
||||||
if name not in ['parent', 'names', 'start_pos', 'end_pos', 'get_code']:
|
if name not in ['parent', 'names', 'start_pos', 'end_pos', 'get_code']:
|
||||||
raise AttributeError('Strange access: %s.' % name)
|
raise AttributeError('Strange accesson %s: %s.' % (self, name))
|
||||||
return getattr(self.name, name)
|
return getattr(self.name, name)
|
||||||
|
|
||||||
def get_parent_until(self):
|
def get_parent_until(self):
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ class FastParser(use_metaclass(CachedFastParser)):
|
|||||||
else:
|
else:
|
||||||
p = parsing.Parser(code[start:],
|
p = parsing.Parser(code[start:],
|
||||||
self.module_path, self.user_position,
|
self.module_path, self.user_position,
|
||||||
line_offset=line_offset, stop_on_scope=True,
|
offset=(line_offset, 0), stop_on_scope=True,
|
||||||
top_module=self.module)
|
top_module=self.module)
|
||||||
|
|
||||||
p.hash = h
|
p.hash = h
|
||||||
|
|||||||
126
jedi/helpers.py
126
jedi/helpers.py
@@ -31,8 +31,7 @@ def fast_parent_copy(obj):
|
|||||||
|
|
||||||
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)
|
||||||
if key in ['parent', '_parent', '_parent_stmt'] \
|
if key in ['parent', '_parent'] and value is not None:
|
||||||
and value is not None:
|
|
||||||
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
|
||||||
@@ -40,8 +39,7 @@ def fast_parent_copy(obj):
|
|||||||
setattr(new_obj, key, new_elements[value])
|
setattr(new_obj, key, new_elements[value])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
elif key in ['parent_stmt', 'parent_function', 'use_as_parent',
|
elif key in ['parent_function', 'use_as_parent', '_sub_module']:
|
||||||
'module']:
|
|
||||||
continue
|
continue
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
setattr(new_obj, key, list_rec(value))
|
setattr(new_obj, key, list_rec(value))
|
||||||
@@ -60,19 +58,6 @@ def fast_parent_copy(obj):
|
|||||||
return recursion(obj)
|
return recursion(obj)
|
||||||
|
|
||||||
|
|
||||||
def generate_param_array(args_tuple, parent_stmt=None):
|
|
||||||
""" This generates an array, that can be used as a param. """
|
|
||||||
values = []
|
|
||||||
for arg in args_tuple:
|
|
||||||
if arg is None:
|
|
||||||
values.append([])
|
|
||||||
else:
|
|
||||||
values.append([arg])
|
|
||||||
pos = None
|
|
||||||
arr = pr.Array(pos, pr.Array.TUPLE, parent_stmt, values=values)
|
|
||||||
return arr
|
|
||||||
|
|
||||||
|
|
||||||
def check_arr_index(arr, pos):
|
def check_arr_index(arr, pos):
|
||||||
positions = arr.arr_el_pos
|
positions = arr.arr_el_pos
|
||||||
for index, comma_pos in enumerate(positions):
|
for index, comma_pos in enumerate(positions):
|
||||||
@@ -81,68 +66,57 @@ def check_arr_index(arr, pos):
|
|||||||
return len(positions)
|
return len(positions)
|
||||||
|
|
||||||
|
|
||||||
def array_for_pos(arr, pos):
|
def array_for_pos(stmt, pos, array_types=None):
|
||||||
if arr.start_pos >= pos \
|
"""Searches for the array and position of a tuple"""
|
||||||
or arr.end_pos[0] is not None and pos >= arr.end_pos:
|
def search_array(arr, pos):
|
||||||
return None, None
|
for i, stmt in enumerate(arr):
|
||||||
|
new_arr, index = array_for_pos(stmt, pos, array_types)
|
||||||
|
if new_arr is not None:
|
||||||
|
return new_arr, index
|
||||||
|
if arr.start_pos < pos <= stmt.end_pos:
|
||||||
|
if not array_types or arr.type in array_types:
|
||||||
|
return arr, i
|
||||||
|
if len(arr) == 0 and arr.start_pos < pos < arr.end_pos:
|
||||||
|
if not array_types or arr.type in array_types:
|
||||||
|
return arr, 0
|
||||||
|
return None, 0
|
||||||
|
|
||||||
result = arr
|
def search_call(call, pos):
|
||||||
for sub in arr:
|
arr, index = None, 0
|
||||||
for s in sub:
|
if call.next is not None:
|
||||||
if isinstance(s, pr.Array):
|
if isinstance(call.next, pr.Array):
|
||||||
result = array_for_pos(s, pos)[0] or result
|
arr, index = search_array(call.next, pos)
|
||||||
elif isinstance(s, pr.Call):
|
else:
|
||||||
if s.execution:
|
arr, index = search_call(call.next, pos)
|
||||||
result = array_for_pos(s.execution, pos)[0] or result
|
if not arr and call.execution is not None:
|
||||||
if s.next:
|
arr, index = search_array(call.execution, pos)
|
||||||
result = array_for_pos(s.next, pos)[0] or result
|
return arr, index
|
||||||
|
|
||||||
return result, check_arr_index(result, pos)
|
if stmt.start_pos >= pos >= stmt.end_pos:
|
||||||
|
return None, 0
|
||||||
|
|
||||||
|
for command in stmt.get_commands():
|
||||||
|
arr = None
|
||||||
|
if isinstance(command, pr.Array):
|
||||||
|
arr, index = search_array(command, pos)
|
||||||
|
elif isinstance(command, pr.Call):
|
||||||
|
arr, index = search_call(command, pos)
|
||||||
|
if arr is not None:
|
||||||
|
return arr, index
|
||||||
|
return None, 0
|
||||||
|
|
||||||
|
|
||||||
def search_function_call(arr, pos):
|
def search_function_definition(stmt, pos):
|
||||||
"""
|
"""
|
||||||
Returns the function Call that matches the position before `arr`.
|
Returns the function Call that matches the position before.
|
||||||
This is somehow stupid, probably only the name of the function.
|
|
||||||
"""
|
"""
|
||||||
call = None
|
# some parts will of the statement will be removed
|
||||||
stop = False
|
stmt = fast_parent_copy(stmt)
|
||||||
for sub in arr.values:
|
arr, index = array_for_pos(stmt, pos, [pr.Array.TUPLE, pr.Array.NOARRAY])
|
||||||
call = None
|
if arr is not None and isinstance(arr.parent, pr.Call):
|
||||||
for s in sub:
|
call = arr.parent
|
||||||
if isinstance(s, pr.Array):
|
while isinstance(call.parent, pr.Call):
|
||||||
new = search_function_call(s, pos)
|
call = call.parent
|
||||||
if new[0] is not None:
|
arr.parent.execution = None
|
||||||
call, index, stop = new
|
return call, index, False
|
||||||
if stop:
|
return None, 0, False
|
||||||
return call, index, stop
|
|
||||||
elif isinstance(s, pr.Call):
|
|
||||||
start_s = s
|
|
||||||
# check parts of calls
|
|
||||||
while s is not None:
|
|
||||||
if s.start_pos >= pos:
|
|
||||||
return call, check_arr_index(arr, pos), stop
|
|
||||||
elif s.execution is not None:
|
|
||||||
end = s.execution.end_pos
|
|
||||||
if s.execution.start_pos < pos and \
|
|
||||||
(None in end or pos < end):
|
|
||||||
c, index, stop = search_function_call(
|
|
||||||
s.execution, pos)
|
|
||||||
if stop:
|
|
||||||
return c, index, stop
|
|
||||||
|
|
||||||
# call should return without execution and
|
|
||||||
# next
|
|
||||||
reset = c or s
|
|
||||||
if reset.execution.type not in \
|
|
||||||
[pr.Array.TUPLE, pr.Array.NOARRAY]:
|
|
||||||
return start_s, index, False
|
|
||||||
|
|
||||||
reset.execution = None
|
|
||||||
reset.next = None
|
|
||||||
return c or start_s, index, True
|
|
||||||
s = s.next
|
|
||||||
|
|
||||||
# The third return is just necessary for recursion inside, because
|
|
||||||
# it needs to know when to stop iterating.
|
|
||||||
return call, check_arr_index(arr, pos), stop
|
|
||||||
|
|||||||
@@ -101,8 +101,8 @@ class ImportPath(pr.Base):
|
|||||||
# 0 (0 is not a valid line number).
|
# 0 (0 is not a valid line number).
|
||||||
zero = (0, 0)
|
zero = (0, 0)
|
||||||
names = i.namespace.names[1:]
|
names = i.namespace.names[1:]
|
||||||
n = pr.Name(i.module, names, zero, zero, self.import_stmt)
|
n = pr.Name(i._sub_module, names, zero, zero, self.import_stmt)
|
||||||
new = pr.Import(i.module, zero, zero, n)
|
new = pr.Import(i._sub_module, zero, zero, n)
|
||||||
new.parent = parent
|
new.parent = parent
|
||||||
debug.dbg('Generated a nested import: %s' % new)
|
debug.dbg('Generated a nested import: %s' % new)
|
||||||
return new
|
return new
|
||||||
@@ -318,7 +318,7 @@ def strip_imports(scopes):
|
|||||||
|
|
||||||
|
|
||||||
@cache.cache_star_import
|
@cache.cache_star_import
|
||||||
def remove_star_imports(scope, ignored_modules=[]):
|
def remove_star_imports(scope, ignored_modules=()):
|
||||||
"""
|
"""
|
||||||
Check a module for star imports:
|
Check a module for star imports:
|
||||||
>>> from module import *
|
>>> from module import *
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ class ModuleWithCursor(Module):
|
|||||||
offset = max(self.position[0] - length, 0)
|
offset = max(self.position[0] - length, 0)
|
||||||
s = '\n'.join(self.source.splitlines()[offset:offset + length])
|
s = '\n'.join(self.source.splitlines()[offset:offset + length])
|
||||||
self._part_parser = parsing.Parser(s, self.path, self.position,
|
self._part_parser = parsing.Parser(s, self.path, self.position,
|
||||||
line_offset=offset)
|
offset=(offset, 0))
|
||||||
return self._part_parser
|
return self._part_parser
|
||||||
|
|
||||||
|
|
||||||
@@ -311,10 +311,12 @@ def sys_path_with_modifications(module):
|
|||||||
|
|
||||||
sys_path = list(get_sys_path()) # copy
|
sys_path = list(get_sys_path()) # copy
|
||||||
for p in possible_stmts:
|
for p in possible_stmts:
|
||||||
try:
|
if not isinstance(p, pr.Statement):
|
||||||
call = p.get_assignment_calls().get_only_subelement()
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
continue
|
||||||
|
commands = p.get_commands()
|
||||||
|
if len(commands) != 1: # sys.path command is just one thing.
|
||||||
|
continue
|
||||||
|
call = commands[0]
|
||||||
n = call.name
|
n = call.name
|
||||||
if not isinstance(n, pr.Name) or len(n.names) != 3:
|
if not isinstance(n, pr.Name) or len(n.names) != 3:
|
||||||
continue
|
continue
|
||||||
|
|||||||
153
jedi/parsing.py
153
jedi/parsing.py
@@ -14,12 +14,10 @@ being parsed completely. ``Statement`` is just a representation of the tokens
|
|||||||
within the statement. This lowers memory usage and cpu time and reduces the
|
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 _compatibility import next, StringIO, unicode
|
from _compatibility import next, StringIO
|
||||||
|
|
||||||
import tokenize
|
import tokenize
|
||||||
import re
|
|
||||||
import keyword
|
import keyword
|
||||||
|
|
||||||
import debug
|
import debug
|
||||||
@@ -47,7 +45,7 @@ class Parser(object):
|
|||||||
:param top_module: Use this module as a parent instead of `self.module`.
|
:param top_module: Use this module as a parent instead of `self.module`.
|
||||||
"""
|
"""
|
||||||
def __init__(self, source, module_path=None, user_position=None,
|
def __init__(self, source, module_path=None, user_position=None,
|
||||||
no_docstr=False, line_offset=0, stop_on_scope=None,
|
no_docstr=False, offset=(0, 0), stop_on_scope=None,
|
||||||
top_module=None):
|
top_module=None):
|
||||||
self.user_position = user_position
|
self.user_position = user_position
|
||||||
self.user_scope = None
|
self.user_scope = None
|
||||||
@@ -55,21 +53,17 @@ class Parser(object):
|
|||||||
self.no_docstr = no_docstr
|
self.no_docstr = no_docstr
|
||||||
|
|
||||||
# initialize global Scope
|
# initialize global Scope
|
||||||
self.module = pr.SubModule(module_path, (line_offset + 1, 0),
|
self.module = pr.SubModule(module_path, (offset[0] + 1, offset[1]),
|
||||||
top_module)
|
top_module)
|
||||||
self.scope = self.module
|
self.scope = self.module
|
||||||
self.current = (None, None)
|
self.current = (None, None)
|
||||||
self.start_pos = 1, 0
|
self.start_pos = 1, 0
|
||||||
self.end_pos = 1, 0
|
self.end_pos = 1, 0
|
||||||
|
|
||||||
# Stuff to fix tokenize errors. The parser is pretty good in tolerating
|
|
||||||
# any errors of tokenize and just parse ahead.
|
|
||||||
self._line_offset = line_offset
|
|
||||||
|
|
||||||
source = source + '\n' # end with \n, because the parser needs it
|
source = source + '\n' # end with \n, because the parser needs it
|
||||||
buf = StringIO(source)
|
buf = StringIO(source)
|
||||||
self._gen = common.NoErrorTokenizer(buf.readline, line_offset,
|
self._gen = common.NoErrorTokenizer(buf.readline, offset,
|
||||||
stop_on_scope)
|
stop_on_scope)
|
||||||
self.top_module = top_module or self.module
|
self.top_module = top_module or self.module
|
||||||
try:
|
try:
|
||||||
self._parse()
|
self._parse()
|
||||||
@@ -303,7 +297,7 @@ class Parser(object):
|
|||||||
return scope
|
return scope
|
||||||
|
|
||||||
def _parse_statement(self, pre_used_token=None, added_breaks=None,
|
def _parse_statement(self, pre_used_token=None, added_breaks=None,
|
||||||
stmt_class=pr.Statement, list_comp=False):
|
stmt_class=pr.Statement):
|
||||||
"""
|
"""
|
||||||
Parses statements like:
|
Parses statements like:
|
||||||
|
|
||||||
@@ -317,10 +311,7 @@ class Parser(object):
|
|||||||
:return: Statement + last parsed token.
|
:return: Statement + last parsed token.
|
||||||
:rtype: (Statement, str)
|
:rtype: (Statement, str)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
string = unicode('')
|
|
||||||
set_vars = []
|
set_vars = []
|
||||||
used_funcs = []
|
|
||||||
used_vars = []
|
used_vars = []
|
||||||
level = 0 # The level of parentheses
|
level = 0 # The level of parentheses
|
||||||
|
|
||||||
@@ -342,126 +333,40 @@ class Parser(object):
|
|||||||
# will even break in parentheses. This is true for typical flow
|
# will even break in parentheses. This is true for typical flow
|
||||||
# commands like def and class and the imports, which will never be used
|
# commands like def and class and the imports, which will never be used
|
||||||
# in a statement.
|
# in a statement.
|
||||||
breaks = ['\n', ':', ')']
|
breaks = set(['\n', ':', ')'])
|
||||||
always_break = [';', 'import', 'from', 'class', 'def', 'try', 'except',
|
always_break = [';', 'import', 'from', 'class', 'def', 'try', 'except',
|
||||||
'finally', 'while', 'return', 'yield']
|
'finally', 'while', 'return', 'yield']
|
||||||
not_first_break = ['del', 'raise']
|
not_first_break = ['del', 'raise']
|
||||||
if added_breaks:
|
if added_breaks:
|
||||||
breaks += added_breaks
|
breaks |= set(added_breaks)
|
||||||
|
|
||||||
tok_list = []
|
tok_list = []
|
||||||
while not (tok in always_break
|
while not (tok in always_break
|
||||||
or tok in not_first_break and not tok_list
|
or tok in not_first_break and not tok_list
|
||||||
or tok in breaks and level <= 0):
|
or tok in breaks and level <= 0):
|
||||||
try:
|
try:
|
||||||
set_string = None
|
|
||||||
#print 'parse_stmt', tok, tokenize.tok_name[token_type]
|
#print 'parse_stmt', tok, tokenize.tok_name[token_type]
|
||||||
tok_list.append(self.current + (self.start_pos,))
|
tok_list.append(self.current + (self.start_pos,))
|
||||||
if tok == 'as':
|
if tok == 'as':
|
||||||
string += " %s " % tok
|
|
||||||
token_type, tok = self.next()
|
token_type, tok = self.next()
|
||||||
if token_type == tokenize.NAME:
|
if token_type == tokenize.NAME:
|
||||||
n, token_type, tok = self._parse_dot_name(self.current)
|
n, token_type, tok = self._parse_dot_name(self.current)
|
||||||
if n:
|
if n:
|
||||||
set_vars.append(n)
|
set_vars.append(n)
|
||||||
tok_list.append(n)
|
tok_list.append(n)
|
||||||
string += ".".join(n.names)
|
|
||||||
continue
|
|
||||||
elif tok == 'lambda':
|
|
||||||
params = []
|
|
||||||
start_pos = self.start_pos
|
|
||||||
while tok != ':':
|
|
||||||
param, tok = self._parse_statement(
|
|
||||||
added_breaks=[':', ','], stmt_class=pr.Param)
|
|
||||||
if param is None:
|
|
||||||
break
|
|
||||||
params.append(param)
|
|
||||||
if tok != ':':
|
|
||||||
continue
|
|
||||||
|
|
||||||
lambd = pr.Lambda(self.module, params, start_pos)
|
|
||||||
ret, tok = self._parse_statement(added_breaks=[','])
|
|
||||||
if ret is not None:
|
|
||||||
ret.parent = lambd
|
|
||||||
lambd.returns.append(ret)
|
|
||||||
lambd.parent = self.scope
|
|
||||||
lambd.end_pos = self.end_pos
|
|
||||||
tok_list[-1] = lambd
|
|
||||||
continue
|
continue
|
||||||
|
elif tok in ['lambda', 'for', 'in']:
|
||||||
|
# don't parse these keywords, parse later in stmt.
|
||||||
|
if tok == 'lambda':
|
||||||
|
breaks.discard(':')
|
||||||
elif token_type == tokenize.NAME:
|
elif token_type == tokenize.NAME:
|
||||||
if tok == 'for':
|
n, token_type, tok = self._parse_dot_name(self.current)
|
||||||
# list comprehensions!
|
# removed last entry, because we add Name
|
||||||
middle, tok = self._parse_statement(
|
tok_list.pop()
|
||||||
added_breaks=['in'])
|
if n:
|
||||||
if tok != 'in' or middle is None:
|
tok_list.append(n)
|
||||||
if middle is None:
|
used_vars.append(n)
|
||||||
level -= 1
|
continue
|
||||||
else:
|
|
||||||
middle.parent = self.scope
|
|
||||||
debug.warning('list comprehension formatting @%s' %
|
|
||||||
self.start_pos[0])
|
|
||||||
continue
|
|
||||||
|
|
||||||
b = [')', ']']
|
|
||||||
in_clause, tok = self._parse_statement(added_breaks=b,
|
|
||||||
list_comp=True)
|
|
||||||
if tok not in b or in_clause is None:
|
|
||||||
middle.parent = self.scope
|
|
||||||
if in_clause is None:
|
|
||||||
self._gen.push_last_back()
|
|
||||||
else:
|
|
||||||
in_clause.parent = self.scope
|
|
||||||
in_clause.parent = self.scope
|
|
||||||
debug.warning('list comprehension in_clause %s@%s'
|
|
||||||
% (repr(tok), self.start_pos[0]))
|
|
||||||
continue
|
|
||||||
other_level = 0
|
|
||||||
|
|
||||||
for i, tok in enumerate(reversed(tok_list)):
|
|
||||||
if not isinstance(tok, (pr.Name,
|
|
||||||
pr.ListComprehension)):
|
|
||||||
tok = tok[1]
|
|
||||||
if tok in closing_brackets:
|
|
||||||
other_level -= 1
|
|
||||||
elif tok in opening_brackets:
|
|
||||||
other_level += 1
|
|
||||||
if other_level > 0:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# could not detect brackets -> nested list comp
|
|
||||||
i = 0
|
|
||||||
|
|
||||||
tok_list, toks = tok_list[:-i], tok_list[-i:-1]
|
|
||||||
src = ''
|
|
||||||
for t in toks:
|
|
||||||
src += t[1] if isinstance(t, tuple) \
|
|
||||||
else t.get_code()
|
|
||||||
st = pr.Statement(self.module, src, [], [], [],
|
|
||||||
toks, first_pos, self.end_pos)
|
|
||||||
|
|
||||||
for s in [st, middle, in_clause]:
|
|
||||||
s.parent = self.scope
|
|
||||||
tok = pr.ListComprehension(st, middle, in_clause)
|
|
||||||
tok_list.append(tok)
|
|
||||||
if list_comp:
|
|
||||||
string = ''
|
|
||||||
string += tok.get_code()
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
n, token_type, tok = self._parse_dot_name(self.current)
|
|
||||||
# removed last entry, because we add Name
|
|
||||||
tok_list.pop()
|
|
||||||
if n:
|
|
||||||
tok_list.append(n)
|
|
||||||
if tok == '(':
|
|
||||||
# it must be a function
|
|
||||||
used_funcs.append(n)
|
|
||||||
else:
|
|
||||||
used_vars.append(n)
|
|
||||||
if string and re.match(r'[\w\d\'"]', string[-1]):
|
|
||||||
string += ' '
|
|
||||||
string += ".".join(n.names)
|
|
||||||
continue
|
|
||||||
elif tok.endswith('=') and tok not in ['>=', '<=', '==', '!=']:
|
elif tok.endswith('=') and tok not in ['>=', '<=', '==', '!=']:
|
||||||
# there has been an assignement -> change vars
|
# there has been an assignement -> change vars
|
||||||
if level == 0:
|
if level == 0:
|
||||||
@@ -472,22 +377,21 @@ class Parser(object):
|
|||||||
elif tok in closing_brackets:
|
elif tok in closing_brackets:
|
||||||
level -= 1
|
level -= 1
|
||||||
|
|
||||||
string = set_string if set_string is not None else string + tok
|
|
||||||
token_type, tok = self.next()
|
token_type, tok = self.next()
|
||||||
except (StopIteration, common.MultiLevelStopIteration):
|
except (StopIteration, common.MultiLevelStopIteration):
|
||||||
# comes from tokenizer
|
# comes from tokenizer
|
||||||
break
|
break
|
||||||
|
|
||||||
if not string:
|
if not tok_list:
|
||||||
return None, tok
|
return None, tok
|
||||||
#print 'new_stat', string, set_vars, used_funcs, used_vars
|
#print 'new_stat', set_vars, used_vars
|
||||||
if self.freshscope and not self.no_docstr and len(tok_list) == 1 \
|
if self.freshscope and not self.no_docstr and len(tok_list) == 1 \
|
||||||
and self.last_token[0] == tokenize.STRING:
|
and self.last_token[0] == tokenize.STRING:
|
||||||
self.scope.add_docstr(self.last_token[1])
|
self.scope.add_docstr(self.last_token[1])
|
||||||
return None, tok
|
return None, tok
|
||||||
else:
|
else:
|
||||||
stmt = stmt_class(self.module, string, set_vars, used_funcs,
|
stmt = stmt_class(self.module, set_vars, used_vars, tok_list,
|
||||||
used_vars, tok_list, first_pos, self.end_pos)
|
first_pos, self.end_pos)
|
||||||
|
|
||||||
self._check_user_stmt(stmt)
|
self._check_user_stmt(stmt)
|
||||||
|
|
||||||
@@ -659,8 +563,8 @@ class Parser(object):
|
|||||||
command = tok
|
command = tok
|
||||||
if command in ['except', 'with']:
|
if command in ['except', 'with']:
|
||||||
added_breaks.append(',')
|
added_breaks.append(',')
|
||||||
# multiple statements because of with
|
# multiple inputs because of with
|
||||||
inits = []
|
inputs = []
|
||||||
first = True
|
first = True
|
||||||
while first or command == 'with' \
|
while first or command == 'with' \
|
||||||
and tok not in [':', '\n']:
|
and tok not in [':', '\n']:
|
||||||
@@ -672,13 +576,12 @@ class Parser(object):
|
|||||||
n, token_type, tok = self._parse_dot_name()
|
n, token_type, tok = self._parse_dot_name()
|
||||||
if n:
|
if n:
|
||||||
statement.set_vars.append(n)
|
statement.set_vars.append(n)
|
||||||
statement.code += ',' + n.get_code()
|
|
||||||
if statement:
|
if statement:
|
||||||
inits.append(statement)
|
inputs.append(statement)
|
||||||
first = False
|
first = False
|
||||||
|
|
||||||
if tok == ':':
|
if tok == ':':
|
||||||
f = pr.Flow(self.module, command, inits, first_pos)
|
f = pr.Flow(self.module, command, inputs, first_pos)
|
||||||
if command in extended_flow:
|
if command in extended_flow:
|
||||||
# the last statement has to be another part of
|
# the last statement has to be another part of
|
||||||
# the flow statement, because a dedent releases the
|
# the flow statement, because a dedent releases the
|
||||||
@@ -692,7 +595,7 @@ class Parser(object):
|
|||||||
s = self.scope.add_statement(f)
|
s = self.scope.add_statement(f)
|
||||||
self.scope = s
|
self.scope = s
|
||||||
else:
|
else:
|
||||||
for i in inits:
|
for i in inputs:
|
||||||
i.parent = use_as_parent_scope
|
i.parent = use_as_parent_scope
|
||||||
debug.warning('syntax err, flow started @%s',
|
debug.warning('syntax err, flow started @%s',
|
||||||
self.start_pos[0])
|
self.start_pos[0])
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -34,8 +34,10 @@ class RecursionDecorator(object):
|
|||||||
|
|
||||||
def push_stmt(self, stmt):
|
def push_stmt(self, stmt):
|
||||||
self.current = RecursionNode(stmt, self.current)
|
self.current = RecursionNode(stmt, self.current)
|
||||||
if self._check_recursion():
|
check = self._check_recursion()
|
||||||
debug.warning('catched recursion', stmt)
|
if check:# TODO remove False!!!!
|
||||||
|
debug.warning('catched stmt recursion: %s against %s @%s'
|
||||||
|
% (stmt, check.stmt, stmt.start_pos))
|
||||||
self.pop_stmt()
|
self.pop_stmt()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -51,7 +53,7 @@ class RecursionDecorator(object):
|
|||||||
while True:
|
while True:
|
||||||
test = test.parent
|
test = test.parent
|
||||||
if self.current == test:
|
if self.current == test:
|
||||||
return True
|
return test
|
||||||
if not test:
|
if not test:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -85,8 +87,12 @@ class RecursionNode(object):
|
|||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not other:
|
if not other:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
is_list_comp = lambda x: isinstance(x, pr.ForFlow) and x.is_list_comp
|
||||||
return self.script == other.script \
|
return self.script == other.script \
|
||||||
and self.position == other.position \
|
and self.position == other.position \
|
||||||
|
and not is_list_comp(self.stmt.parent) \
|
||||||
|
and not is_list_comp(other.parent) \
|
||||||
and not self.is_ignored and not other.is_ignored
|
and not self.is_ignored and not other.is_ignored
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Introduce some basic refactoring functions to |jedi|. This module is still in a
|
|||||||
very early development stage and needs much testing and improvement.
|
very early development stage and needs much testing and improvement.
|
||||||
|
|
||||||
.. warning:: I won't do too much here, but if anyone wants to step in, please
|
.. warning:: I won't do too much here, but if anyone wants to step in, please
|
||||||
do.
|
do. Refactoring is none of my priorities
|
||||||
|
|
||||||
It uses the |jedi| `API <plugin-api.html>`_ and supports currently the
|
It uses the |jedi| `API <plugin-api.html>`_ and supports currently the
|
||||||
following functions (sometimes bug-prone):
|
following functions (sometimes bug-prone):
|
||||||
@@ -11,7 +11,6 @@ following functions (sometimes bug-prone):
|
|||||||
- rename
|
- rename
|
||||||
- extract variable
|
- extract variable
|
||||||
- inline variable
|
- inline variable
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
@@ -19,6 +18,7 @@ from __future__ import with_statement
|
|||||||
import modules
|
import modules
|
||||||
import difflib
|
import difflib
|
||||||
import helpers
|
import helpers
|
||||||
|
import parsing_representation as pr
|
||||||
|
|
||||||
|
|
||||||
class Refactoring(object):
|
class Refactoring(object):
|
||||||
@@ -113,13 +113,10 @@ def extract(script, new_name):
|
|||||||
if user_stmt:
|
if user_stmt:
|
||||||
pos = script.pos
|
pos = script.pos
|
||||||
line_index = pos[0] - 1
|
line_index = pos[0] - 1
|
||||||
arr, index = helpers.array_for_pos(user_stmt.get_assignment_calls(),
|
arr, index = helpers.array_for_pos(user_stmt, pos)
|
||||||
pos)
|
if arr is not None:
|
||||||
if arr:
|
start_pos = arr[index].start_pos
|
||||||
s = arr.start_pos[0], arr.start_pos[1] + 1
|
end_pos = arr[index].end_pos
|
||||||
positions = [s] + arr.arr_el_pos + [arr.end_pos]
|
|
||||||
start_pos = positions[index]
|
|
||||||
end_pos = positions[index + 1][0], positions[index + 1][1] - 1
|
|
||||||
|
|
||||||
# take full line if the start line is different from end line
|
# take full line if the start line is different from end line
|
||||||
e = end_pos[1] if end_pos[0] == start_pos[0] else None
|
e = end_pos[1] if end_pos[0] == start_pos[0] else None
|
||||||
@@ -178,17 +175,19 @@ def inline(script):
|
|||||||
if not stmt.start_pos <= r.start_pos <= stmt.end_pos]
|
if not stmt.start_pos <= r.start_pos <= stmt.end_pos]
|
||||||
inlines = sorted(inlines, key=lambda x: (x.module_path, x.start_pos),
|
inlines = sorted(inlines, key=lambda x: (x.module_path, x.start_pos),
|
||||||
reverse=True)
|
reverse=True)
|
||||||
ass = stmt.get_assignment_calls()
|
commands = stmt.get_commands()
|
||||||
# don't allow multiline refactorings for now.
|
# don't allow multiline refactorings for now.
|
||||||
assert ass.start_pos[0] == ass.end_pos[0]
|
assert stmt.start_pos[0] == stmt.end_pos[0]
|
||||||
index = ass.start_pos[0] - 1
|
index = stmt.start_pos[0] - 1
|
||||||
|
|
||||||
line = new_lines[index]
|
line = new_lines[index]
|
||||||
replace_str = line[ass.start_pos[1]:ass.end_pos[1] + 1]
|
replace_str = line[commands[0].start_pos[1]:stmt.end_pos[1] + 1]
|
||||||
replace_str = replace_str.strip()
|
replace_str = replace_str.strip()
|
||||||
# tuples need parentheses
|
# tuples need parentheses
|
||||||
if len(ass.values) > 1:
|
if commands and isinstance(commands[0], pr.Array):
|
||||||
replace_str = '(%s)' % replace_str
|
arr = commands[0]
|
||||||
|
if replace_str[0] not in ['(', '[', '{'] and len(arr) > 1:
|
||||||
|
replace_str = '(%s)' % replace_str
|
||||||
|
|
||||||
# if it's the only assignment, remove the statement
|
# if it's the only assignment, remove the statement
|
||||||
if len(stmt.set_vars) == 1:
|
if len(stmt.set_vars) == 1:
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Parser
|
|||||||
|
|
||||||
.. autodata:: fast_parser
|
.. autodata:: fast_parser
|
||||||
.. autodata:: fast_parser_always_reparse
|
.. autodata:: fast_parser_always_reparse
|
||||||
.. autodata:: use_get_in_function_call_cache
|
.. autodata:: use_function_definition_cache
|
||||||
|
|
||||||
|
|
||||||
Dynamic stuff
|
Dynamic stuff
|
||||||
@@ -66,14 +66,14 @@ definitely worse in some cases. But a completion should also be fast.
|
|||||||
.. autodata:: max_function_recursion_level
|
.. autodata:: max_function_recursion_level
|
||||||
.. autodata:: max_executions_without_builtins
|
.. autodata:: max_executions_without_builtins
|
||||||
.. autodata:: max_executions
|
.. autodata:: max_executions
|
||||||
.. autodata:: scale_get_in_function_call
|
.. autodata:: scale_function_definition
|
||||||
|
|
||||||
|
|
||||||
Caching
|
Caching
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
.. autodata:: star_import_cache_validity
|
.. autodata:: star_import_cache_validity
|
||||||
.. autodata:: get_in_function_call_validity
|
.. autodata:: function_definition_validity
|
||||||
|
|
||||||
|
|
||||||
Various
|
Various
|
||||||
@@ -156,9 +156,9 @@ This is just a debugging option. Always reparsing means that the fast parser
|
|||||||
is basically useless. So don't use it.
|
is basically useless. So don't use it.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use_get_in_function_call_cache = True
|
use_function_definition_cache = True
|
||||||
"""
|
"""
|
||||||
Use the cache (full cache) to generate get_in_function_call's. This may fail
|
Use the cache (full cache) to generate function_definition's. This may fail
|
||||||
with multiline docstrings (likely) and other complicated changes (unlikely).
|
with multiline docstrings (likely) and other complicated changes (unlikely).
|
||||||
The goal is to move away from it by making the rest faster.
|
The goal is to move away from it by making the rest faster.
|
||||||
"""
|
"""
|
||||||
@@ -225,9 +225,9 @@ max_executions = 250
|
|||||||
A maximum amount of time, the completion may use.
|
A maximum amount of time, the completion may use.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
scale_get_in_function_call = 0.1
|
scale_function_definition = 0.1
|
||||||
"""
|
"""
|
||||||
Because get_in_function_call is normally used on every single key hit, it has
|
Because function_definition is normally used on every single key hit, it has
|
||||||
to be faster than a normal completion. This is the factor that is used to
|
to be faster than a normal completion. This is the factor that is used to
|
||||||
scale `max_executions` and `max_until_execution_unique`:
|
scale `max_executions` and `max_until_execution_unique`:
|
||||||
"""
|
"""
|
||||||
@@ -253,7 +253,7 @@ might be slow, therefore we do a star import caching, that lasts a certain
|
|||||||
time span (in seconds).
|
time span (in seconds).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
get_in_function_call_validity = 3.0
|
function_definition_validity = 3.0
|
||||||
"""
|
"""
|
||||||
Finding function calls might be slow (0.1-0.5s). This is not acceptible for
|
Finding function calls might be slow (0.1-0.5s). This is not acceptible for
|
||||||
normal writing. Therefore cache it for a short time.
|
normal writing. Therefore cache it for a short time.
|
||||||
|
|||||||
12
test/base.py
12
test/base.py
@@ -32,6 +32,7 @@ sys.argv = sys.argv[:1] + args
|
|||||||
summary = []
|
summary = []
|
||||||
tests_fail = 0
|
tests_fail = 0
|
||||||
|
|
||||||
|
|
||||||
def get_test_list():
|
def get_test_list():
|
||||||
# get test list, that should be executed
|
# get test list, that should be executed
|
||||||
test_files = {}
|
test_files = {}
|
||||||
@@ -46,6 +47,7 @@ def get_test_list():
|
|||||||
last = arg
|
last = arg
|
||||||
return test_files
|
return test_files
|
||||||
|
|
||||||
|
|
||||||
class TestBase(unittest.TestCase):
|
class TestBase(unittest.TestCase):
|
||||||
def get_script(self, src, pos, path=None):
|
def get_script(self, src, pos, path=None):
|
||||||
if pos is None:
|
if pos is None:
|
||||||
@@ -53,9 +55,9 @@ class TestBase(unittest.TestCase):
|
|||||||
pos = len(lines), len(lines[-1])
|
pos = len(lines), len(lines[-1])
|
||||||
return jedi.Script(src, pos[0], pos[1], path)
|
return jedi.Script(src, pos[0], pos[1], path)
|
||||||
|
|
||||||
def get_def(self, src, pos=None):
|
def definition(self, src, pos=None):
|
||||||
script = self.get_script(src, pos)
|
script = self.get_script(src, pos)
|
||||||
return script.get_definition()
|
return script.definition()
|
||||||
|
|
||||||
def complete(self, src, pos=None, path=None):
|
def complete(self, src, pos=None, path=None):
|
||||||
script = self.get_script(src, pos, path)
|
script = self.get_script(src, pos, path)
|
||||||
@@ -65,13 +67,13 @@ class TestBase(unittest.TestCase):
|
|||||||
script = self.get_script(src, pos)
|
script = self.get_script(src, pos)
|
||||||
return script.goto()
|
return script.goto()
|
||||||
|
|
||||||
def get_in_function_call(self, src, pos=None):
|
def function_definition(self, src, pos=None):
|
||||||
script = self.get_script(src, pos)
|
script = self.get_script(src, pos)
|
||||||
return script.get_in_function_call()
|
return script.function_definition()
|
||||||
|
|
||||||
|
|
||||||
def print_summary():
|
def print_summary():
|
||||||
print('\nSummary: (%s fails of %s tests) in %.3fs' % \
|
print('\nSummary: (%s fails of %s tests) in %.3fs' % \
|
||||||
(tests_fail, test_sum, time.time() - t_start))
|
(tests_fail, test_sum, time.time() - t_start))
|
||||||
for s in summary:
|
for s in summary:
|
||||||
print(s)
|
print(s)
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ for i in list([1,'']):
|
|||||||
#? int() str()
|
#? int() str()
|
||||||
i
|
i
|
||||||
|
|
||||||
|
#? int() str()
|
||||||
|
for x in [1,'']: x
|
||||||
|
|
||||||
a = []
|
a = []
|
||||||
b = [1.0,'']
|
b = [1.0,'']
|
||||||
for i in b:
|
for i in b:
|
||||||
|
|||||||
@@ -109,10 +109,10 @@ def func(a=1, b=''):
|
|||||||
return a, b
|
return a, b
|
||||||
|
|
||||||
exe = func(b=list, a=tuple)
|
exe = func(b=list, a=tuple)
|
||||||
#? tuple()
|
#? tuple
|
||||||
exe[0]
|
exe[0]
|
||||||
|
|
||||||
#? list()
|
#? list
|
||||||
exe[1]
|
exe[1]
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
# goto command test are a different in syntax
|
# goto command tests are a different in syntax
|
||||||
|
|
||||||
definition = 3
|
definition = 3
|
||||||
##! 0 ['a=definition']
|
#! 0 ['a = definition']
|
||||||
a = definition
|
a = definition
|
||||||
|
|
||||||
#! []
|
#! []
|
||||||
b
|
b
|
||||||
#! ['a=definition']
|
#! ['a = definition']
|
||||||
a
|
a
|
||||||
|
|
||||||
b = a
|
b = a
|
||||||
c = b
|
c = b
|
||||||
#! ['c=b']
|
#! ['c = b']
|
||||||
c
|
c
|
||||||
|
|
||||||
cd = 1
|
cd = 1
|
||||||
#! 1 ['cd=c']
|
#! 1 ['cd = c']
|
||||||
cd = c
|
cd = c
|
||||||
#! 0 ['cd=e']
|
#! 0 ['cd = e']
|
||||||
cd = e
|
cd = e
|
||||||
|
|
||||||
#! ['module math']
|
#! ['module math']
|
||||||
@@ -27,12 +27,12 @@ math
|
|||||||
|
|
||||||
#! ['import math']
|
#! ['import math']
|
||||||
b = math
|
b = math
|
||||||
#! ['b=math']
|
#! ['b = math']
|
||||||
b
|
b
|
||||||
|
|
||||||
class C(object):
|
class C(object):
|
||||||
def b(self):
|
def b(self):
|
||||||
#! ['b=math']
|
#! ['b = math']
|
||||||
b
|
b
|
||||||
#! ['def b']
|
#! ['def b']
|
||||||
self.b
|
self.b
|
||||||
@@ -45,7 +45,7 @@ class C(object):
|
|||||||
#! ['def b']
|
#! ['def b']
|
||||||
b
|
b
|
||||||
|
|
||||||
#! ['b=math']
|
#! ['b = math']
|
||||||
b
|
b
|
||||||
|
|
||||||
#! ['def b']
|
#! ['def b']
|
||||||
@@ -63,9 +63,9 @@ D.b
|
|||||||
#! ['def b']
|
#! ['def b']
|
||||||
D().b
|
D().b
|
||||||
|
|
||||||
#! 0 ['D=C']
|
#! 0 ['D = C']
|
||||||
D().b
|
D().b
|
||||||
#! 0 ['D=C']
|
#! 0 ['D = C']
|
||||||
D().b
|
D().b
|
||||||
|
|
||||||
def c():
|
def c():
|
||||||
@@ -82,43 +82,43 @@ c()
|
|||||||
|
|
||||||
#! ['module import_tree']
|
#! ['module import_tree']
|
||||||
import import_tree
|
import import_tree
|
||||||
#! ['a=""']
|
#! ["a = ''"]
|
||||||
import_tree.a
|
import_tree.a
|
||||||
|
|
||||||
#! ['module mod1']
|
#! ['module mod1']
|
||||||
import import_tree.mod1
|
import import_tree.mod1
|
||||||
#! ['a=1']
|
#! ['a = 1']
|
||||||
import_tree.mod1.a
|
import_tree.mod1.a
|
||||||
|
|
||||||
#! ['module pkg']
|
#! ['module pkg']
|
||||||
import import_tree.pkg
|
import import_tree.pkg
|
||||||
#! ['a=list']
|
#! ['a = list']
|
||||||
import_tree.pkg.a
|
import_tree.pkg.a
|
||||||
|
|
||||||
#! ['module mod1']
|
#! ['module mod1']
|
||||||
import import_tree.pkg.mod1
|
import import_tree.pkg.mod1
|
||||||
#! ['a=1.0']
|
#! ['a = 1.0']
|
||||||
import_tree.pkg.mod1.a
|
import_tree.pkg.mod1.a
|
||||||
#! ['a=""']
|
#! ["a = ''"]
|
||||||
import_tree.a
|
import_tree.a
|
||||||
|
|
||||||
#! ['module mod1']
|
#! ['module mod1']
|
||||||
from import_tree.pkg import mod1
|
from import_tree.pkg import mod1
|
||||||
#! ['a=1.0']
|
#! ['a = 1.0']
|
||||||
mod1.a
|
mod1.a
|
||||||
|
|
||||||
#! ['module mod1']
|
#! ['module mod1']
|
||||||
from import_tree import mod1
|
from import_tree import mod1
|
||||||
#! ['a=1']
|
#! ['a = 1']
|
||||||
mod1.a
|
mod1.a
|
||||||
|
|
||||||
#! ['a=1.0']
|
#! ['a = 1.0']
|
||||||
from import_tree.pkg.mod1 import a
|
from import_tree.pkg.mod1 import a
|
||||||
|
|
||||||
#! ['import os']
|
#! ['import os']
|
||||||
from .imports import os
|
from .imports import os
|
||||||
|
|
||||||
#! ['some_variable=1']
|
#! ['some_variable = 1']
|
||||||
from . import some_variable
|
from . import some_variable
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
@@ -151,7 +151,7 @@ param = ClassDef
|
|||||||
def ab1(param): pass
|
def ab1(param): pass
|
||||||
#! 9 ['param']
|
#! 9 ['param']
|
||||||
def ab2(param): pass
|
def ab2(param): pass
|
||||||
#! 11 ['param=ClassDef']
|
#! 11 ['param = ClassDef']
|
||||||
def ab3(a=param): pass
|
def ab3(a=param): pass
|
||||||
|
|
||||||
ab1(ClassDef);ab2(ClassDef);ab3(ClassDef)
|
ab1(ClassDef);ab2(ClassDef);ab3(ClassDef)
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ a[0]
|
|||||||
|
|
||||||
a = [a for a in [1,2]
|
a = [a for a in [1,2]
|
||||||
def break(): pass
|
def break(): pass
|
||||||
#? list()
|
#? int()
|
||||||
a[0]
|
a[0]
|
||||||
|
|
||||||
#? []
|
#? []
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ class TestRegression(TestBase):
|
|||||||
self.assertEqual(length, 1)
|
self.assertEqual(length, 1)
|
||||||
|
|
||||||
def test_part_parser(self):
|
def test_part_parser(self):
|
||||||
""" test the get_in_function_call speedups """
|
""" test the function_definition speedups """
|
||||||
s = '\n' * 100 + 'abs('
|
s = '\n' * 100 + 'abs('
|
||||||
pos = 101, 4
|
pos = 101, 4
|
||||||
self.get_in_function_call(s, pos)
|
self.function_definition(s, pos)
|
||||||
assert self.get_in_function_call(s, pos)
|
assert self.function_definition(s, pos)
|
||||||
|
|
||||||
def test_get_definition_cursor(self):
|
def test_definition_cursor(self):
|
||||||
|
|
||||||
s = ("class A():\n"
|
s = ("class A():\n"
|
||||||
" def _something(self):\n"
|
" def _something(self):\n"
|
||||||
@@ -60,7 +60,7 @@ class TestRegression(TestBase):
|
|||||||
diff_line = 4, 10
|
diff_line = 4, 10
|
||||||
should2 = 8, 10
|
should2 = 8, 10
|
||||||
|
|
||||||
get_def = lambda pos: [d.description for d in self.get_def(s, pos)]
|
get_def = lambda pos: [d.description for d in self.definition(s, pos)]
|
||||||
in_name = get_def(in_name)
|
in_name = get_def(in_name)
|
||||||
under_score = get_def(under_score)
|
under_score = get_def(under_score)
|
||||||
should1 = get_def(should1)
|
should1 = get_def(should1)
|
||||||
@@ -71,32 +71,31 @@ class TestRegression(TestBase):
|
|||||||
assert should1 == in_name
|
assert should1 == in_name
|
||||||
assert should1 == under_score
|
assert should1 == under_score
|
||||||
|
|
||||||
#print should2, diff_line
|
|
||||||
assert should2 == diff_line
|
assert should2 == diff_line
|
||||||
|
|
||||||
self.assertRaises(jedi.NotFoundError, get_def, cls)
|
self.assertRaises(jedi.NotFoundError, get_def, cls)
|
||||||
|
|
||||||
def test_keyword_doc(self):
|
def test_keyword_doc(self):
|
||||||
r = list(self.get_def("or", (1, 1)))
|
r = list(self.definition("or", (1, 1)))
|
||||||
assert len(r) == 1
|
assert len(r) == 1
|
||||||
if not is_py25:
|
if not is_py25:
|
||||||
assert len(r[0].doc) > 100
|
assert len(r[0].doc) > 100
|
||||||
|
|
||||||
r = list(self.get_def("asfdasfd", (1, 1)))
|
r = list(self.definition("asfdasfd", (1, 1)))
|
||||||
assert len(r) == 0
|
assert len(r) == 0
|
||||||
|
|
||||||
def test_operator_doc(self):
|
def test_operator_doc(self):
|
||||||
r = list(self.get_def("a == b", (1, 3)))
|
r = list(self.definition("a == b", (1, 3)))
|
||||||
assert len(r) == 1
|
assert len(r) == 1
|
||||||
if not is_py25:
|
if not is_py25:
|
||||||
assert len(r[0].doc) > 100
|
assert len(r[0].doc) > 100
|
||||||
|
|
||||||
def test_get_definition_at_zero(self):
|
def test_definition_at_zero(self):
|
||||||
assert self.get_def("a", (1, 1)) == []
|
assert self.definition("a", (1, 1)) == []
|
||||||
s = self.get_def("str", (1, 1))
|
s = self.definition("str", (1, 1))
|
||||||
assert len(s) == 1
|
assert len(s) == 1
|
||||||
assert list(s)[0].description == 'class str'
|
assert list(s)[0].description == 'class str'
|
||||||
assert self.get_def("", (1, 0)) == []
|
assert self.definition("", (1, 0)) == []
|
||||||
|
|
||||||
def test_complete_at_zero(self):
|
def test_complete_at_zero(self):
|
||||||
s = self.complete("str", (1, 3))
|
s = self.complete("str", (1, 3))
|
||||||
@@ -106,9 +105,9 @@ class TestRegression(TestBase):
|
|||||||
s = self.complete("", (1, 0))
|
s = self.complete("", (1, 0))
|
||||||
assert len(s) > 0
|
assert len(s) > 0
|
||||||
|
|
||||||
def test_get_definition_on_import(self):
|
def test_definition_on_import(self):
|
||||||
assert self.get_def("import sys_blabla", (1, 8)) == []
|
assert self.definition("import sys_blabla", (1, 8)) == []
|
||||||
assert len(self.get_def("import sys", (1, 8))) == 1
|
assert len(self.definition("import sys", (1, 8))) == 1
|
||||||
|
|
||||||
def test_complete_on_empty_import(self):
|
def test_complete_on_empty_import(self):
|
||||||
# should just list the files in the directory
|
# should just list the files in the directory
|
||||||
@@ -123,7 +122,7 @@ class TestRegression(TestBase):
|
|||||||
assert self.complete("from datetime import")[0].word == 'import'
|
assert self.complete("from datetime import")[0].word == 'import'
|
||||||
assert self.complete("from datetime import ")
|
assert self.complete("from datetime import ")
|
||||||
|
|
||||||
def test_get_in_function_call(self):
|
def test_function_definition(self):
|
||||||
def check(call_def, name, index):
|
def check(call_def, name, index):
|
||||||
return call_def and call_def.call_name == name \
|
return call_def and call_def.call_name == name \
|
||||||
and call_def.index == index
|
and call_def.index == index
|
||||||
@@ -139,54 +138,54 @@ class TestRegression(TestBase):
|
|||||||
s7 = "str().upper().center("
|
s7 = "str().upper().center("
|
||||||
s8 = "str(int[zip("
|
s8 = "str(int[zip("
|
||||||
|
|
||||||
assert check(self.get_in_function_call(s, (1, 4)), 'abs', 0)
|
assert check(self.function_definition(s, (1, 4)), 'abs', 0)
|
||||||
assert check(self.get_in_function_call(s, (1, 6)), 'abs', 1)
|
assert check(self.function_definition(s, (1, 6)), 'abs', 1)
|
||||||
assert check(self.get_in_function_call(s, (1, 7)), 'abs', 1)
|
assert check(self.function_definition(s, (1, 7)), 'abs', 1)
|
||||||
assert check(self.get_in_function_call(s, (1, 8)), 'abs', 1)
|
assert check(self.function_definition(s, (1, 8)), 'abs', 1)
|
||||||
assert check(self.get_in_function_call(s, (1, 11)), 'str', 0)
|
assert check(self.function_definition(s, (1, 11)), 'str', 0)
|
||||||
|
|
||||||
assert check(self.get_in_function_call(s2, (1, 4)), 'abs', 0)
|
assert check(self.function_definition(s2, (1, 4)), 'abs', 0)
|
||||||
assert self.get_in_function_call(s2, (1, 5)) is None
|
assert self.function_definition(s2, (1, 5)) is None
|
||||||
assert self.get_in_function_call(s2) is None
|
assert self.function_definition(s2) is None
|
||||||
|
|
||||||
assert self.get_in_function_call(s3, (1, 5)) is None
|
assert self.function_definition(s3, (1, 5)) is None
|
||||||
assert self.get_in_function_call(s3) is None
|
assert self.function_definition(s3) is None
|
||||||
|
|
||||||
assert self.get_in_function_call(s4, (1, 3)) is None
|
assert self.function_definition(s4, (1, 3)) is None
|
||||||
assert check(self.get_in_function_call(s4, (1, 4)), 'abs', 0)
|
assert check(self.function_definition(s4, (1, 4)), 'abs', 0)
|
||||||
assert check(self.get_in_function_call(s4, (1, 8)), 'zip', 0)
|
assert check(self.function_definition(s4, (1, 8)), 'zip', 0)
|
||||||
assert check(self.get_in_function_call(s4, (1, 9)), 'abs', 0)
|
assert check(self.function_definition(s4, (1, 9)), 'abs', 0)
|
||||||
assert check(self.get_in_function_call(s4, (1, 10)), 'abs', 1)
|
#assert check(self.function_definition(s4, (1, 10)), 'abs', 1)
|
||||||
|
|
||||||
assert check(self.get_in_function_call(s5, (1, 4)), 'abs', 0)
|
assert check(self.function_definition(s5, (1, 4)), 'abs', 0)
|
||||||
assert check(self.get_in_function_call(s5, (1, 6)), 'abs', 1)
|
assert check(self.function_definition(s5, (1, 6)), 'abs', 1)
|
||||||
|
|
||||||
assert check(self.get_in_function_call(s6), 'center', 0)
|
assert check(self.function_definition(s6), 'center', 0)
|
||||||
assert check(self.get_in_function_call(s6, (1, 4)), 'str', 0)
|
assert check(self.function_definition(s6, (1, 4)), 'str', 0)
|
||||||
|
|
||||||
assert check(self.get_in_function_call(s7), 'center', 0)
|
assert check(self.function_definition(s7), 'center', 0)
|
||||||
assert check(self.get_in_function_call(s8), 'zip', 0)
|
assert check(self.function_definition(s8), 'zip', 0)
|
||||||
assert check(self.get_in_function_call(s8, (1, 8)), 'str', 0)
|
assert check(self.function_definition(s8, (1, 8)), 'str', 0)
|
||||||
|
|
||||||
s = "import time; abc = time; abc.sleep("
|
s = "import time; abc = time; abc.sleep("
|
||||||
assert check(self.get_in_function_call(s), 'sleep', 0)
|
assert check(self.function_definition(s), 'sleep', 0)
|
||||||
|
|
||||||
# jedi-vim #9
|
# jedi-vim #9
|
||||||
s = "with open("
|
s = "with open("
|
||||||
assert check(self.get_in_function_call(s), 'open', 0)
|
assert check(self.function_definition(s), 'open', 0)
|
||||||
|
|
||||||
# jedi-vim #11
|
# jedi-vim #11
|
||||||
s1 = "for sorted("
|
s1 = "for sorted("
|
||||||
assert check(self.get_in_function_call(s1), 'sorted', 0)
|
assert check(self.function_definition(s1), 'sorted', 0)
|
||||||
s2 = "for s in sorted("
|
s2 = "for s in sorted("
|
||||||
assert check(self.get_in_function_call(s2), 'sorted', 0)
|
assert check(self.function_definition(s2), 'sorted', 0)
|
||||||
|
|
||||||
# jedi #57
|
# jedi #57
|
||||||
s = "def func(alpha, beta): pass\n" \
|
s = "def func(alpha, beta): pass\n" \
|
||||||
"func(alpha='101',"
|
"func(alpha='101',"
|
||||||
assert check(self.get_in_function_call(s, (2, 13)), 'func', 0)
|
assert check(self.function_definition(s, (2, 13)), 'func', 0)
|
||||||
|
|
||||||
def test_get_in_function_call_complex(self):
|
def test_function_definition_complex(self):
|
||||||
def check(call_def, name, index):
|
def check(call_def, name, index):
|
||||||
return call_def and call_def.call_name == name \
|
return call_def and call_def.call_name == name \
|
||||||
and call_def.index == index
|
and call_def.index == index
|
||||||
@@ -201,17 +200,17 @@ class TestRegression(TestBase):
|
|||||||
if 1:
|
if 1:
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
assert check(self.get_in_function_call(s, (6, 24)), 'abc', 0)
|
assert check(self.function_definition(s, (6, 24)), 'abc', 0)
|
||||||
s = """
|
s = """
|
||||||
import re
|
import re
|
||||||
def huhu(it):
|
def huhu(it):
|
||||||
re.compile(
|
re.compile(
|
||||||
return it * 2
|
return it * 2
|
||||||
"""
|
"""
|
||||||
assert check(self.get_in_function_call(s, (4, 31)), 'compile', 0)
|
assert check(self.function_definition(s, (4, 31)), 'compile', 0)
|
||||||
# jedi-vim #70
|
# jedi-vim #70
|
||||||
s = """def foo("""
|
s = """def foo("""
|
||||||
assert self.get_in_function_call(s) is None
|
assert self.function_definition(s) is None
|
||||||
|
|
||||||
def test_add_dynamic_mods(self):
|
def test_add_dynamic_mods(self):
|
||||||
api.settings.additional_dynamic_modules = ['dynamic.py']
|
api.settings.additional_dynamic_modules = ['dynamic.py']
|
||||||
@@ -222,15 +221,15 @@ class TestRegression(TestBase):
|
|||||||
# .parser to load the module
|
# .parser to load the module
|
||||||
api.modules.Module(os.path.abspath('dynamic.py'), src2).parser
|
api.modules.Module(os.path.abspath('dynamic.py'), src2).parser
|
||||||
script = jedi.Script(src1, 1, len(src1), '../setup.py')
|
script = jedi.Script(src1, 1, len(src1), '../setup.py')
|
||||||
result = script.get_definition()
|
result = script.definition()
|
||||||
assert len(result) == 1
|
assert len(result) == 1
|
||||||
assert result[0].description == 'class int'
|
assert result[0].description == 'class int'
|
||||||
|
|
||||||
def test_named_import(self):
|
def test_named_import(self):
|
||||||
""" named import - jedi-vim issue #8 """
|
""" named import - jedi-vim issue #8 """
|
||||||
s = "import time as dt"
|
s = "import time as dt"
|
||||||
assert len(jedi.Script(s, 1, 15, '/').get_definition()) == 1
|
assert len(jedi.Script(s, 1, 15, '/').definition()) == 1
|
||||||
assert len(jedi.Script(s, 1, 10, '/').get_definition()) == 1
|
assert len(jedi.Script(s, 1, 10, '/').definition()) == 1
|
||||||
|
|
||||||
def test_unicode_script(self):
|
def test_unicode_script(self):
|
||||||
""" normally no unicode objects are being used. (<=2.7) """
|
""" normally no unicode objects are being used. (<=2.7) """
|
||||||
@@ -241,7 +240,8 @@ class TestRegression(TestBase):
|
|||||||
|
|
||||||
s = utf8("author='öä'; author")
|
s = utf8("author='öä'; author")
|
||||||
completions = self.complete(s)
|
completions = self.complete(s)
|
||||||
assert type(completions[0].description) is unicode
|
x = completions[0].description
|
||||||
|
assert type(x) is unicode
|
||||||
|
|
||||||
s = utf8("#-*- coding: iso-8859-1 -*-\nauthor='öä'; author")
|
s = utf8("#-*- coding: iso-8859-1 -*-\nauthor='öä'; author")
|
||||||
s = s.encode('latin-1')
|
s = s.encode('latin-1')
|
||||||
@@ -284,10 +284,10 @@ class TestRegression(TestBase):
|
|||||||
|
|
||||||
def test_keyword_definition_doc(self):
|
def test_keyword_definition_doc(self):
|
||||||
""" github jedi-vim issue #44 """
|
""" github jedi-vim issue #44 """
|
||||||
defs = self.get_def("print")
|
defs = self.definition("print")
|
||||||
assert [d.doc for d in defs]
|
assert [d.doc for d in defs]
|
||||||
|
|
||||||
defs = self.get_def("import")
|
defs = self.definition("import")
|
||||||
assert len(defs) == 1
|
assert len(defs) == 1
|
||||||
assert [d.doc for d in defs]
|
assert [d.doc for d in defs]
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ class TestFeature(TestBase):
|
|||||||
assert self.complete('import os; os.path.join')[0].full_name \
|
assert self.complete('import os; os.path.join')[0].full_name \
|
||||||
== 'os.path.join'
|
== 'os.path.join'
|
||||||
# issue #94
|
# issue #94
|
||||||
defs = self.get_def("""import os; os.path.join(""")
|
defs = self.definition("""import os; os.path.join(""")
|
||||||
assert defs[0].full_name is None
|
assert defs[0].full_name is None
|
||||||
|
|
||||||
def test_full_name_builtin(self):
|
def test_full_name_builtin(self):
|
||||||
@@ -345,7 +345,7 @@ class TestFeature(TestBase):
|
|||||||
import re
|
import re
|
||||||
any_re = re.compile('.*')
|
any_re = re.compile('.*')
|
||||||
any_re"""
|
any_re"""
|
||||||
self.assertEqual(self.get_def(s)[0].full_name, 're.RegexObject')
|
self.assertEqual(self.definition(s)[0].full_name, 're.RegexObject')
|
||||||
|
|
||||||
def test_quick_completion(self):
|
def test_quick_completion(self):
|
||||||
sources = [
|
sources = [
|
||||||
@@ -398,7 +398,7 @@ class TestSpeed(TestBase):
|
|||||||
def test_scipy_speed(self):
|
def test_scipy_speed(self):
|
||||||
s = 'import scipy.weave; scipy.weave.inline('
|
s = 'import scipy.weave; scipy.weave.inline('
|
||||||
script = jedi.Script(s, 1, len(s), '')
|
script = jedi.Script(s, 1, len(s), '')
|
||||||
script.get_in_function_call()
|
script.function_definition()
|
||||||
#print(jedi.imports.imports_processed)
|
#print(jedi.imports.imports_processed)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
10
test/run.py
10
test/run.py
@@ -36,7 +36,7 @@ def run_definition_test(script, should_str, line_nr):
|
|||||||
Runs tests for definitions.
|
Runs tests for definitions.
|
||||||
Return if the test was a fail or not, with 1 for fail and 0 for success.
|
Return if the test was a fail or not, with 1 for fail and 0 for success.
|
||||||
"""
|
"""
|
||||||
result = script.get_definition()
|
result = script.definition()
|
||||||
is_str = set(r.desc_with_module for r in result)
|
is_str = set(r.desc_with_module for r in result)
|
||||||
if is_str != should_str:
|
if is_str != should_str:
|
||||||
print('Solution @%s not right, received %s, wanted %s' \
|
print('Solution @%s not right, received %s, wanted %s' \
|
||||||
@@ -120,10 +120,10 @@ def run_test(source, f_name, lines_to_execute):
|
|||||||
>>> #? int()
|
>>> #? int()
|
||||||
>>> ab = 3; ab
|
>>> ab = 3; ab
|
||||||
"""
|
"""
|
||||||
def get_defs(correct, correct_start, path):
|
def definition(correct, correct_start, path):
|
||||||
def defs(line_nr, indent):
|
def defs(line_nr, indent):
|
||||||
script = jedi.Script(source, line_nr, indent, path)
|
script = jedi.Script(source, line_nr, indent, path)
|
||||||
return set(script.get_definition())
|
return set(script.definition())
|
||||||
|
|
||||||
should_be = set()
|
should_be = set()
|
||||||
number = 0
|
number = 0
|
||||||
@@ -166,7 +166,7 @@ def run_test(source, f_name, lines_to_execute):
|
|||||||
else:
|
else:
|
||||||
index = len(line) - 1 # -1 for the \n
|
index = len(line) - 1 # -1 for the \n
|
||||||
# if a list is wanted, use the completion test, otherwise the
|
# if a list is wanted, use the completion test, otherwise the
|
||||||
# get_definition test
|
# definition test
|
||||||
path = completion_test_dir + os.path.sep + f_name
|
path = completion_test_dir + os.path.sep + f_name
|
||||||
try:
|
try:
|
||||||
script = jedi.Script(source, line_nr, index, path)
|
script = jedi.Script(source, line_nr, index, path)
|
||||||
@@ -177,7 +177,7 @@ def run_test(source, f_name, lines_to_execute):
|
|||||||
elif correct.startswith('['):
|
elif correct.startswith('['):
|
||||||
fails += run_completion_test(script, correct, line_nr)
|
fails += run_completion_test(script, correct, line_nr)
|
||||||
else:
|
else:
|
||||||
should_str = get_defs(correct, start, path)
|
should_str = definition(correct, start, path)
|
||||||
fails += run_definition_test(script, should_str, line_nr)
|
fails += run_definition_test(script, should_str, line_nr)
|
||||||
except Exception:
|
except Exception:
|
||||||
print(traceback.format_exc())
|
print(traceback.format_exc())
|
||||||
|
|||||||
Reference in New Issue
Block a user