forked from VimPlug/jedi
Merge branch 'dev'
This commit is contained in:
@@ -43,6 +43,7 @@ bstaint (@bstaint)
|
|||||||
Mathias Rav (@Mortal) <rav@cs.au.dk>
|
Mathias Rav (@Mortal) <rav@cs.au.dk>
|
||||||
Daniel Fiterman (@dfit99) <fitermandaniel2@gmail.com>
|
Daniel Fiterman (@dfit99) <fitermandaniel2@gmail.com>
|
||||||
Simon Ruggier (@sruggier)
|
Simon Ruggier (@sruggier)
|
||||||
|
Élie Gouzien (@ElieGouzien)
|
||||||
|
|
||||||
|
|
||||||
Note: (@user) means a github user name.
|
Note: (@user) means a github user name.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ All nodes and leaves have these methods/properties:
|
|||||||
|
|
||||||
|
|
||||||
Python Parser Tree
|
Python Parser Tree
|
||||||
-----------------
|
------------------
|
||||||
|
|
||||||
.. automodule:: jedi.parser.python.tree
|
.. automodule:: jedi.parser.python.tree
|
||||||
:members:
|
:members:
|
||||||
|
|||||||
@@ -166,14 +166,6 @@ try:
|
|||||||
except NameError:
|
except NameError:
|
||||||
unicode = str
|
unicode = str
|
||||||
|
|
||||||
if is_py3:
|
|
||||||
u = lambda s: s
|
|
||||||
else:
|
|
||||||
u = lambda s: s.decode('utf-8')
|
|
||||||
|
|
||||||
u.__doc__ = """
|
|
||||||
Decode a raw string into unicode object. Do nothing in Python 3.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# exec function
|
# exec function
|
||||||
if is_py3:
|
if is_py3:
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ class Script(object):
|
|||||||
elif isinstance(node, tree.Import):
|
elif isinstance(node, tree.Import):
|
||||||
import_names = set(node.get_defined_names())
|
import_names = set(node.get_defined_names())
|
||||||
if node.is_nested():
|
if node.is_nested():
|
||||||
import_names |= set(path[-1] for path in node.paths())
|
import_names |= set(path[-1] for path in node.get_paths())
|
||||||
for n in import_names:
|
for n in import_names:
|
||||||
imports.infer_import(context, n)
|
imports.infer_import(context, n)
|
||||||
elif node.type == 'expr_stmt':
|
elif node.type == 'expr_stmt':
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from jedi.evaluate import representation as er
|
|||||||
from jedi.evaluate import instance
|
from jedi.evaluate import instance
|
||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate.filters import ParamName, TreeNameDefinition
|
from jedi.evaluate.filters import ParamName
|
||||||
from jedi.evaluate.imports import ImportName
|
from jedi.evaluate.imports import ImportName
|
||||||
from jedi.api.keywords import KeywordName
|
from jedi.api.keywords import KeywordName
|
||||||
|
|
||||||
@@ -538,7 +538,12 @@ class Definition(BaseDefinition):
|
|||||||
typ = 'def'
|
typ = 'def'
|
||||||
return typ + ' ' + u(self._name.string_name)
|
return typ + ' ' + u(self._name.string_name)
|
||||||
elif typ == 'param':
|
elif typ == 'param':
|
||||||
return typ + ' ' + tree_name.get_definition().get_description()
|
code = tree_name.get_definition().get_code(
|
||||||
|
include_prefix=False,
|
||||||
|
include_comma=False
|
||||||
|
)
|
||||||
|
return typ + ' ' + code
|
||||||
|
|
||||||
|
|
||||||
definition = tree_name.get_definition()
|
definition = tree_name.get_definition()
|
||||||
# Remove the prefix, because that's not what we want for get_code
|
# Remove the prefix, because that's not what we want for get_code
|
||||||
|
|||||||
@@ -76,12 +76,16 @@ class KeywordName(AbstractNameDefinition):
|
|||||||
api_type = 'keyword'
|
api_type = 'keyword'
|
||||||
|
|
||||||
def __init__(self, evaluator, name):
|
def __init__(self, evaluator, name):
|
||||||
|
self.evaluator = evaluator
|
||||||
self.string_name = name
|
self.string_name = name
|
||||||
self.parent_context = evaluator.BUILTINS
|
self.parent_context = evaluator.BUILTINS
|
||||||
|
|
||||||
def eval(self):
|
def eval(self):
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
|
def infer(self):
|
||||||
|
return [Keyword(self.evaluator, self.string_name, (0, 0))]
|
||||||
|
|
||||||
|
|
||||||
class Keyword(object):
|
class Keyword(object):
|
||||||
api_type = 'keyword'
|
api_type = 'keyword'
|
||||||
@@ -100,9 +104,8 @@ class Keyword(object):
|
|||||||
""" For a `parsing.Name` like comparision """
|
""" For a `parsing.Name` like comparision """
|
||||||
return [self.name]
|
return [self.name]
|
||||||
|
|
||||||
@property
|
def py__doc__(self, include_call_signature=False):
|
||||||
def docstr(self):
|
return imitate_pydoc(self.name.string_name)
|
||||||
return imitate_pydoc(self.name)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<%s: %s>' % (type(self).__name__, self.name)
|
return '<%s: %s>' % (type(self).__name__, self.name)
|
||||||
@@ -136,6 +139,6 @@ def imitate_pydoc(string):
|
|||||||
return ''
|
return ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return pydoc_topics.topics[label] if pydoc_topics else ''
|
return pydoc_topics.topics[label].strip() if pydoc_topics else ''
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return ''
|
return ''
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ class Evaluator(object):
|
|||||||
if_stmt = if_stmt.parent
|
if_stmt = if_stmt.parent
|
||||||
if if_stmt.type in ('if_stmt', 'for_stmt'):
|
if if_stmt.type in ('if_stmt', 'for_stmt'):
|
||||||
break
|
break
|
||||||
if if_stmt.is_scope():
|
if parser_utils.is_scope(if_stmt):
|
||||||
if_stmt = None
|
if_stmt = None
|
||||||
break
|
break
|
||||||
predefined_if_name_dict = context.predefined_names.get(if_stmt)
|
predefined_if_name_dict = context.predefined_names.get(if_stmt)
|
||||||
@@ -337,7 +337,7 @@ class Evaluator(object):
|
|||||||
# This is the first global lookup.
|
# This is the first global lookup.
|
||||||
stmt = atom.get_definition()
|
stmt = atom.get_definition()
|
||||||
if stmt.type == 'comp_for':
|
if stmt.type == 'comp_for':
|
||||||
stmt = tree.search_ancestor(stmt, ('expr_stmt', 'lambdef', 'funcdef', 'classdef'))
|
stmt = tree.search_ancestor(stmt, 'expr_stmt', 'lambdef', 'funcdef', 'classdef')
|
||||||
if stmt is None or stmt.type != 'expr_stmt':
|
if stmt is None or stmt.type != 'expr_stmt':
|
||||||
# We only need to adjust the start_pos for statements, because
|
# We only need to adjust the start_pos for statements, because
|
||||||
# there the name cannot be used.
|
# there the name cannot be used.
|
||||||
@@ -537,7 +537,7 @@ class Evaluator(object):
|
|||||||
while True:
|
while True:
|
||||||
node = node.parent
|
node = node.parent
|
||||||
|
|
||||||
if node.is_scope():
|
if parser_utils.is_scope(node):
|
||||||
return node
|
return node
|
||||||
elif node.type in ('argument', 'testlist_comp'):
|
elif node.type in ('argument', 'testlist_comp'):
|
||||||
if node.children[1].type == 'comp_for':
|
if node.children[1].type == 'comp_for':
|
||||||
@@ -553,7 +553,7 @@ class Evaluator(object):
|
|||||||
return base_context
|
return base_context
|
||||||
|
|
||||||
is_funcdef = scope_node.type in ('funcdef', 'lambdef')
|
is_funcdef = scope_node.type in ('funcdef', 'lambdef')
|
||||||
parent_scope = scope_node.get_parent_scope()
|
parent_scope = parser_utils.get_parent_scope(scope_node)
|
||||||
parent_context = from_scope_node(parent_scope, child_is_funcdef=is_funcdef)
|
parent_context = from_scope_node(parent_scope, child_is_funcdef=is_funcdef)
|
||||||
|
|
||||||
if is_funcdef:
|
if is_funcdef:
|
||||||
@@ -586,7 +586,7 @@ class Evaluator(object):
|
|||||||
|
|
||||||
base_node = base_context.tree_node
|
base_node = base_context.tree_node
|
||||||
|
|
||||||
if node_is_context and node.is_scope():
|
if node_is_context and parser_utils.is_scope(node):
|
||||||
scope_node = node
|
scope_node = node
|
||||||
else:
|
else:
|
||||||
if node.parent.type in ('funcdef', 'classdef'):
|
if node.parent.type in ('funcdef', 'classdef'):
|
||||||
|
|||||||
@@ -222,9 +222,6 @@ class CompiledObject(Context):
|
|||||||
for result in self.evaluator.execute(bltn_obj, params):
|
for result in self.evaluator.execute(bltn_obj, params):
|
||||||
yield result
|
yield result
|
||||||
|
|
||||||
def is_scope(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_self_attributes(self):
|
def get_self_attributes(self):
|
||||||
return [] # Instance compatibility
|
return [] # Instance compatibility
|
||||||
|
|
||||||
|
|||||||
@@ -116,8 +116,21 @@ def _load_module(evaluator, path, python_object):
|
|||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def source_findable(python_object):
|
||||||
|
"""Check if inspect.getfile has a chance to find the source."""
|
||||||
|
return (inspect.ismodule(python_object) or
|
||||||
|
inspect.isclass(python_object) or
|
||||||
|
inspect.ismethod(python_object) or
|
||||||
|
inspect.isfunction(python_object) or
|
||||||
|
inspect.istraceback(python_object) or
|
||||||
|
inspect.isframe(python_object) or
|
||||||
|
inspect.iscode(python_object))
|
||||||
|
|
||||||
|
|
||||||
def find_syntax_node_name(evaluator, python_object):
|
def find_syntax_node_name(evaluator, python_object):
|
||||||
try:
|
try:
|
||||||
|
if not source_findable(python_object):
|
||||||
|
raise TypeError # Prevents computation of `repr` within inspect.
|
||||||
path = inspect.getsourcefile(python_object)
|
path = inspect.getsourcefile(python_object)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# The type might not be known (e.g. class_with_dict.__weakref__)
|
# The type might not be known (e.g. class_with_dict.__weakref__)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from jedi.evaluate.cache import memoize_default
|
|||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
from jedi.evaluate.param import TreeArguments, create_default_param
|
from jedi.evaluate.param import TreeArguments, create_default_param
|
||||||
from jedi.common import to_list, unite
|
from jedi.common import to_list, unite
|
||||||
|
from jedi.parser_utils import get_parent_scope
|
||||||
|
|
||||||
|
|
||||||
MAX_PARAM_SEARCHES = 20
|
MAX_PARAM_SEARCHES = 20
|
||||||
@@ -103,7 +104,7 @@ def _search_function_executions(evaluator, module_context, funcdef):
|
|||||||
func_string_name = funcdef.name.value
|
func_string_name = funcdef.name.value
|
||||||
compare_node = funcdef
|
compare_node = funcdef
|
||||||
if func_string_name == '__init__':
|
if func_string_name == '__init__':
|
||||||
cls = funcdef.get_parent_scope()
|
cls = get_parent_scope(funcdef)
|
||||||
if isinstance(cls, tree.Class):
|
if isinstance(cls, tree.Class):
|
||||||
func_string_name = cls.name.value
|
func_string_name = cls.name.value
|
||||||
compare_node = cls
|
compare_node = cls
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from abc import abstractmethod
|
|||||||
from jedi.parser.tree import search_ancestor
|
from jedi.parser.tree import search_ancestor
|
||||||
from jedi.evaluate import flow_analysis
|
from jedi.evaluate import flow_analysis
|
||||||
from jedi.common import to_list, unite
|
from jedi.common import to_list, unite
|
||||||
|
from jedi.parser_utils import get_parent_scope
|
||||||
|
|
||||||
|
|
||||||
class AbstractNameDefinition(object):
|
class AbstractNameDefinition(object):
|
||||||
@@ -189,7 +190,7 @@ class ParserTreeFilter(AbstractUsedNamesFilter):
|
|||||||
if parent.type == 'trailer':
|
if parent.type == 'trailer':
|
||||||
return False
|
return False
|
||||||
base_node = parent if parent.type in ('classdef', 'funcdef') else name
|
base_node = parent if parent.type in ('classdef', 'funcdef') else name
|
||||||
return base_node.get_parent_scope() == self._parser_scope
|
return get_parent_scope(base_node) == self._parser_scope
|
||||||
|
|
||||||
def _check_flows(self, names):
|
def _check_flows(self, names):
|
||||||
for name in sorted(names, key=lambda name: name.start_pos, reverse=True):
|
for name in sorted(names, key=lambda name: name.start_pos, reverse=True):
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from jedi.evaluate import param
|
|||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
from jedi.evaluate.filters import get_global_filters
|
from jedi.evaluate.filters import get_global_filters
|
||||||
from jedi.evaluate.context import ContextualizedName, ContextualizedNode
|
from jedi.evaluate.context import ContextualizedName, ContextualizedNode
|
||||||
|
from jedi.parser_utils import is_scope, get_parent_scope
|
||||||
|
|
||||||
|
|
||||||
class NameFinder(object):
|
class NameFinder(object):
|
||||||
@@ -106,7 +107,7 @@ class NameFinder(object):
|
|||||||
if self._context.predefined_names:
|
if self._context.predefined_names:
|
||||||
# TODO is this ok? node might not always be a tree.Name
|
# TODO is this ok? node might not always be a tree.Name
|
||||||
node = self._name
|
node = self._name
|
||||||
while node is not None and not node.is_scope():
|
while node is not None and not is_scope(node):
|
||||||
node = node.parent
|
node = node.parent
|
||||||
if node.type in ("if_stmt", "for_stmt", "comp_for"):
|
if node.type in ("if_stmt", "for_stmt", "comp_for"):
|
||||||
try:
|
try:
|
||||||
@@ -160,7 +161,7 @@ class NameFinder(object):
|
|||||||
if base_node.type == 'comp_for':
|
if base_node.type == 'comp_for':
|
||||||
return types
|
return types
|
||||||
while True:
|
while True:
|
||||||
flow_scope = flow_scope.get_parent_scope(include_flows=True)
|
flow_scope = get_parent_scope(flow_scope, include_flows=True)
|
||||||
n = _check_flow_information(self._name_context, flow_scope,
|
n = _check_flow_information(self._name_context, flow_scope,
|
||||||
self._name, self._position)
|
self._name, self._position)
|
||||||
if n is not None:
|
if n is not None:
|
||||||
@@ -298,7 +299,7 @@ def _check_flow_information(context, flow, search_name, pos):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
if flow.is_scope():
|
if is_scope(flow):
|
||||||
# Check for asserts.
|
# Check for asserts.
|
||||||
module_node = flow.get_root_node()
|
module_node = flow.get_root_node()
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from jedi.parser_utils import get_flow_branch_keyword
|
from jedi.parser_utils import get_flow_branch_keyword, is_scope, get_parent_scope
|
||||||
|
|
||||||
|
|
||||||
class Status(object):
|
class Status(object):
|
||||||
lookup_table = {}
|
lookup_table = {}
|
||||||
|
|
||||||
@@ -32,14 +34,14 @@ UNSURE = Status(None, 'unsure')
|
|||||||
|
|
||||||
def _get_flow_scopes(node):
|
def _get_flow_scopes(node):
|
||||||
while True:
|
while True:
|
||||||
node = node.get_parent_scope(include_flows=True)
|
node = get_parent_scope(node, include_flows=True)
|
||||||
if node is None or node.is_scope():
|
if node is None or is_scope(node):
|
||||||
return
|
return
|
||||||
yield node
|
yield node
|
||||||
|
|
||||||
|
|
||||||
def reachability_check(context, context_scope, node, origin_scope=None):
|
def reachability_check(context, context_scope, node, origin_scope=None):
|
||||||
first_flow_scope = node.get_parent_scope(include_flows=True)
|
first_flow_scope = get_parent_scope(node, include_flows=True)
|
||||||
if origin_scope is not None:
|
if origin_scope is not None:
|
||||||
origin_flow_scopes = list(_get_flow_scopes(origin_scope))
|
origin_flow_scopes = list(_get_flow_scopes(origin_scope))
|
||||||
node_flow_scopes = list(_get_flow_scopes(node))
|
node_flow_scopes = list(_get_flow_scopes(node))
|
||||||
@@ -95,7 +97,7 @@ def _break_check(context, context_scope, flow_scope, node):
|
|||||||
return reachable
|
return reachable
|
||||||
|
|
||||||
if context_scope != flow_scope and context_scope != flow_scope.parent:
|
if context_scope != flow_scope and context_scope != flow_scope.parent:
|
||||||
flow_scope = flow_scope.get_parent_scope(include_flows=True)
|
flow_scope = get_parent_scope(flow_scope, include_flows=True)
|
||||||
return reachable & _break_check(context, context_scope, flow_scope, node)
|
return reachable & _break_check(context, context_scope, flow_scope, node)
|
||||||
else:
|
else:
|
||||||
return reachable
|
return reachable
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from itertools import chain
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from jedi.parser.python import tree
|
from jedi.parser.python import tree
|
||||||
|
from jedi.parser_utils import get_parent_scope
|
||||||
|
|
||||||
|
|
||||||
def deep_ast_copy(obj):
|
def deep_ast_copy(obj):
|
||||||
@@ -143,7 +144,7 @@ def get_module_names(module, all_scopes):
|
|||||||
# parent_scope. There's None as a parent, because nodes in the module
|
# parent_scope. There's None as a parent, because nodes in the module
|
||||||
# node have the parent module and not suite as all the others.
|
# node have the parent module and not suite as all the others.
|
||||||
# Therefore it's important to catch that case.
|
# Therefore it's important to catch that case.
|
||||||
names = [n for n in names if n.get_parent_scope().parent in (module, None)]
|
names = [n for n in names if get_parent_scope(n).parent in (module, None)]
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ from jedi.evaluate.filters import AbstractNameDefinition
|
|||||||
@memoize_default(default=set())
|
@memoize_default(default=set())
|
||||||
def infer_import(context, tree_name, is_goto=False):
|
def infer_import(context, tree_name, is_goto=False):
|
||||||
module_context = context.get_root_context()
|
module_context = context.get_root_context()
|
||||||
import_node = search_ancestor(tree_name, ('import_name', 'import_from'))
|
import_node = search_ancestor(tree_name, 'import_name', 'import_from')
|
||||||
import_path = import_node.get_path_for_name(tree_name)
|
import_path = import_node.get_path_for_name(tree_name)
|
||||||
from_import_name = None
|
from_import_name = None
|
||||||
evaluator = context.evaluator
|
evaluator = context.evaluator
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from jedi.cache import memoize_method
|
|||||||
from jedi.evaluate import representation as er
|
from jedi.evaluate import representation as er
|
||||||
from jedi.evaluate.dynamic import search_params
|
from jedi.evaluate.dynamic import search_params
|
||||||
from jedi.evaluate import iterable
|
from jedi.evaluate import iterable
|
||||||
|
from jedi.parser_utils import get_parent_scope
|
||||||
|
|
||||||
|
|
||||||
class AbstractInstanceContext(Context):
|
class AbstractInstanceContext(Context):
|
||||||
@@ -151,7 +152,7 @@ class AbstractInstanceContext(Context):
|
|||||||
def create_instance_context(self, class_context, node):
|
def create_instance_context(self, class_context, node):
|
||||||
if node.parent.type in ('funcdef', 'classdef'):
|
if node.parent.type in ('funcdef', 'classdef'):
|
||||||
node = node.parent
|
node = node.parent
|
||||||
scope = node.get_parent_scope()
|
scope = get_parent_scope(node)
|
||||||
if scope == class_context.tree_node:
|
if scope == class_context.tree_node:
|
||||||
return class_context
|
return class_context
|
||||||
else:
|
else:
|
||||||
@@ -189,13 +190,18 @@ class CompiledInstance(AbstractInstanceContext):
|
|||||||
return compiled.CompiledContextName(self, self.class_context.name.string_name)
|
return compiled.CompiledContextName(self, self.class_context.name.string_name)
|
||||||
|
|
||||||
def create_instance_context(self, class_context, node):
|
def create_instance_context(self, class_context, node):
|
||||||
if node.get_parent_scope().type == 'classdef':
|
if get_parent_scope(node).type == 'classdef':
|
||||||
return class_context
|
return class_context
|
||||||
else:
|
else:
|
||||||
return super(CompiledInstance, self).create_instance_context(class_context, node)
|
return super(CompiledInstance, self).create_instance_context(class_context, node)
|
||||||
|
|
||||||
|
|
||||||
class TreeInstance(AbstractInstanceContext):
|
class TreeInstance(AbstractInstanceContext):
|
||||||
|
def __init__(self, evaluator, parent_context, class_context, var_args):
|
||||||
|
super(TreeInstance, self).__init__(evaluator, parent_context,
|
||||||
|
class_context, var_args)
|
||||||
|
self.tree_node = class_context.tree_node
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return filters.ContextName(self, self.class_context.name.tree_name)
|
return filters.ContextName(self, self.class_context.name.tree_name)
|
||||||
@@ -332,7 +338,7 @@ class InstanceClassFilter(filters.ParserTreeFilter):
|
|||||||
while node is not None:
|
while node is not None:
|
||||||
if node == self._parser_scope or node == self.context:
|
if node == self._parser_scope or node == self.context:
|
||||||
return True
|
return True
|
||||||
node = node.get_parent_scope()
|
node = get_parent_scope(node)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _access_possible(self, name):
|
def _access_possible(self, name):
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
|||||||
def py__class__(self):
|
def py__class__(self):
|
||||||
# This differentiation is only necessary for Python2. Python3 does not
|
# This differentiation is only necessary for Python2. Python3 does not
|
||||||
# use a different method class.
|
# use a different method class.
|
||||||
if isinstance(self.tree_node.get_parent_scope(), tree.Class):
|
if isinstance(parser_utils.get_parent_scope(self.tree_node), tree.Class):
|
||||||
name = 'METHOD_CLASS'
|
name = 'METHOD_CLASS'
|
||||||
else:
|
else:
|
||||||
name = 'FUNCTION_CLASS'
|
name = 'FUNCTION_CLASS'
|
||||||
@@ -350,8 +350,8 @@ class FunctionExecutionContext(context.TreeContext):
|
|||||||
|
|
||||||
@recursion.execution_recursion_decorator(default=iter([]))
|
@recursion.execution_recursion_decorator(default=iter([]))
|
||||||
def get_yield_values(self):
|
def get_yield_values(self):
|
||||||
for_parents = [(y, tree.search_ancestor(y, ('for_stmt', 'funcdef',
|
for_parents = [(y, tree.search_ancestor(y, 'for_stmt', 'funcdef',
|
||||||
'while_stmt', 'if_stmt')))
|
'while_stmt', 'if_stmt'))
|
||||||
for y in self.tree_node.iter_yield_exprs()]
|
for y in self.tree_node.iter_yield_exprs()]
|
||||||
|
|
||||||
# Calculate if the yields are placed within the same for loop.
|
# Calculate if the yields are placed within the same for loop.
|
||||||
@@ -476,7 +476,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
|||||||
modules = []
|
modules = []
|
||||||
for i in self.tree_node.iter_imports():
|
for i in self.tree_node.iter_imports():
|
||||||
if i.is_star_import():
|
if i.is_star_import():
|
||||||
name = i.star_import_name()
|
name = i.get_paths()[-1][-1]
|
||||||
new = imports.infer_import(self, name)
|
new = imports.infer_import(self, name)
|
||||||
for module in new:
|
for module in new:
|
||||||
if isinstance(module, ModuleContext):
|
if isinstance(module, ModuleContext):
|
||||||
|
|||||||
@@ -74,9 +74,6 @@ def parse(code=None, path=None, grammar=None, error_recovery=True,
|
|||||||
if grammar is None:
|
if grammar is None:
|
||||||
grammar = load_grammar()
|
grammar = load_grammar()
|
||||||
|
|
||||||
if path is not None:
|
|
||||||
path = os.path.expanduser(path)
|
|
||||||
|
|
||||||
if cache and not code and path is not None:
|
if cache and not code and path is not None:
|
||||||
# In this case we do actual caching. We just try to load it.
|
# In this case we do actual caching. We just try to load it.
|
||||||
module_node = load_module(grammar, path)
|
module_node = load_module(grammar, path)
|
||||||
|
|||||||
@@ -144,22 +144,9 @@ class Parser(BaseParser):
|
|||||||
elif symbol == 'suite' and len(nodes) > 1:
|
elif symbol == 'suite' and len(nodes) > 1:
|
||||||
# suites without an indent in them get discarded.
|
# suites without an indent in them get discarded.
|
||||||
break
|
break
|
||||||
elif symbol == 'simple_stmt' and len(nodes) > 1:
|
|
||||||
# simple_stmt can just be turned into a PythonNode, if
|
|
||||||
# there are enough statements. Ignore the rest after that.
|
|
||||||
break
|
|
||||||
return index, symbol, nodes
|
return index, symbol, nodes
|
||||||
|
|
||||||
index, symbol, nodes = current_suite(stack)
|
index, symbol, nodes = current_suite(stack)
|
||||||
if symbol == 'simple_stmt':
|
|
||||||
index -= 2
|
|
||||||
(_, _, (type_, suite_nodes)) = stack[index]
|
|
||||||
symbol = grammar.number2symbol[type_]
|
|
||||||
suite_nodes.append(tree.PythonNode(symbol, list(nodes)))
|
|
||||||
# Remove
|
|
||||||
nodes[:] = []
|
|
||||||
nodes = suite_nodes
|
|
||||||
stack[index]
|
|
||||||
|
|
||||||
# print('err', token.tok_name[typ], repr(value), start_pos, len(stack), index)
|
# print('err', token.tok_name[typ], repr(value), start_pos, len(stack), index)
|
||||||
if self._stack_removal(grammar, stack, arcs, index + 1, value, start_pos):
|
if self._stack_removal(grammar, stack, arcs, index + 1, value, start_pos):
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ Any subclasses of :class:`Scope`, including :class:`Module` has an attribute
|
|||||||
[<ImportName: import os@1,0>]
|
[<ImportName: import os@1,0>]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from itertools import chain
|
|
||||||
|
|
||||||
from jedi._compatibility import utf8_repr, unicode
|
from jedi._compatibility import utf8_repr, unicode
|
||||||
from jedi.parser.tree import Node, BaseNode, Leaf, ErrorNode, ErrorLeaf, \
|
from jedi.parser.tree import Node, BaseNode, Leaf, ErrorNode, ErrorLeaf, \
|
||||||
search_ancestor
|
search_ancestor
|
||||||
@@ -61,18 +59,10 @@ class DocstringMixin(object):
|
|||||||
|
|
||||||
|
|
||||||
class PythonMixin(object):
|
class PythonMixin(object):
|
||||||
def get_parent_scope(self, include_flows=False):
|
"""
|
||||||
"""
|
Some Python specific utitilies.
|
||||||
Returns the underlying scope.
|
"""
|
||||||
"""
|
__slots__ = ()
|
||||||
scope = self.parent
|
|
||||||
while scope is not None:
|
|
||||||
if include_flows and isinstance(scope, Flow):
|
|
||||||
return scope
|
|
||||||
if scope.is_scope():
|
|
||||||
break
|
|
||||||
scope = scope.parent
|
|
||||||
return scope
|
|
||||||
|
|
||||||
def get_definition(self):
|
def get_definition(self):
|
||||||
if self.type in ('newline', 'endmarker'):
|
if self.type in ('newline', 'endmarker'):
|
||||||
@@ -92,10 +82,6 @@ class PythonMixin(object):
|
|||||||
break
|
break
|
||||||
return scope
|
return scope
|
||||||
|
|
||||||
def is_scope(self):
|
|
||||||
# Default is not being a scope. Just inherit from Scope.
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_name_of_position(self, position):
|
def get_name_of_position(self, position):
|
||||||
for c in self.children:
|
for c in self.children:
|
||||||
if isinstance(c, Leaf):
|
if isinstance(c, Leaf):
|
||||||
@@ -269,9 +255,6 @@ class Scope(PythonBaseNode, DocstringMixin):
|
|||||||
|
|
||||||
return scan(self.children)
|
return scan(self.children)
|
||||||
|
|
||||||
def is_scope(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_suite(self):
|
def get_suite(self):
|
||||||
"""
|
"""
|
||||||
Returns the part that is executed by the function.
|
Returns the part that is executed by the function.
|
||||||
@@ -311,7 +294,7 @@ class Module(Scope):
|
|||||||
# the future print statement).
|
# the future print statement).
|
||||||
for imp in self.iter_imports():
|
for imp in self.iter_imports():
|
||||||
if imp.type == 'import_from' and imp.level == 0:
|
if imp.type == 'import_from' and imp.level == 0:
|
||||||
for path in imp.paths():
|
for path in imp.get_paths():
|
||||||
names = [name.value for name in path]
|
names = [name.value for name in path]
|
||||||
if len(names) == 2 and names[0] == '__future__':
|
if len(names) == 2 and names[0] == '__future__':
|
||||||
yield names[1]
|
yield names[1]
|
||||||
@@ -328,6 +311,10 @@ class Module(Scope):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def get_used_names(self):
|
def get_used_names(self):
|
||||||
|
"""
|
||||||
|
Returns all the `Name` leafs that exist in this module. Tihs includes
|
||||||
|
both definitions and references of names.
|
||||||
|
"""
|
||||||
if self._used_names is None:
|
if self._used_names is None:
|
||||||
# Don't directly use self._used_names to eliminate a lookup.
|
# Don't directly use self._used_names to eliminate a lookup.
|
||||||
dct = {}
|
dct = {}
|
||||||
@@ -358,9 +345,15 @@ class ClassOrFunc(Scope):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
"""
|
||||||
|
Returns the `Name` leaf that defines the function or class name.
|
||||||
|
"""
|
||||||
return self.children[1]
|
return self.children[1]
|
||||||
|
|
||||||
def get_decorators(self):
|
def get_decorators(self):
|
||||||
|
"""
|
||||||
|
:return list of Decorator:
|
||||||
|
"""
|
||||||
decorated = self.parent
|
decorated = self.parent
|
||||||
if decorated.type == 'decorated':
|
if decorated.type == 'decorated':
|
||||||
if decorated.children[0].type == 'decorators':
|
if decorated.children[0].type == 'decorators':
|
||||||
@@ -389,6 +382,10 @@ class Class(ClassOrFunc):
|
|||||||
super(Class, self).__init__(children)
|
super(Class, self).__init__(children)
|
||||||
|
|
||||||
def get_super_arglist(self):
|
def get_super_arglist(self):
|
||||||
|
"""
|
||||||
|
Returns the `arglist` node that defines the super classes. It returns
|
||||||
|
None if there are no arguments.
|
||||||
|
"""
|
||||||
if self.children[2] != '(': # Has no parentheses
|
if self.children[2] != '(': # Has no parentheses
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -452,14 +449,15 @@ class Function(ClassOrFunc):
|
|||||||
"""
|
"""
|
||||||
Used to store the parsed contents of a python function.
|
Used to store the parsed contents of a python function.
|
||||||
|
|
||||||
Children:
|
Children::
|
||||||
0. <Keyword: def>
|
|
||||||
1. <Name>
|
0. <Keyword: def>
|
||||||
2. parameter list (including open-paren and close-paren <Operator>s)
|
1. <Name>
|
||||||
3. or 5. <Operator: :>
|
2. parameter list (including open-paren and close-paren <Operator>s)
|
||||||
4. or 6. Node() representing function body
|
3. or 5. <Operator: :>
|
||||||
3. -> (if annotation is also present)
|
4. or 6. Node() representing function body
|
||||||
4. annotation (if present)
|
3. -> (if annotation is also present)
|
||||||
|
4. annotation (if present)
|
||||||
"""
|
"""
|
||||||
type = 'funcdef'
|
type = 'funcdef'
|
||||||
|
|
||||||
@@ -514,20 +512,16 @@ class Function(ClassOrFunc):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_paramlist_code(self):
|
|
||||||
return self.children[2].get_code()
|
|
||||||
|
|
||||||
|
|
||||||
class Lambda(Function):
|
class Lambda(Function):
|
||||||
"""
|
"""
|
||||||
Lambdas are basically trimmed functions, so give it the same interface.
|
Lambdas are basically trimmed functions, so give it the same interface.
|
||||||
|
|
||||||
Children:
|
Children::
|
||||||
|
|
||||||
0. <Keyword: lambda>
|
0. <Keyword: lambda>
|
||||||
*. <Param x> for each argument x
|
*. <Param x> for each argument x
|
||||||
-2. <Operator: :>
|
-2. <Operator: :>
|
||||||
-1. Node() representing body
|
-1. Node() representing body
|
||||||
"""
|
"""
|
||||||
type = 'lambdef'
|
type = 'lambdef'
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
@@ -545,9 +539,6 @@ class Lambda(Function):
|
|||||||
"""
|
"""
|
||||||
raise AttributeError("lambda is not named.")
|
raise AttributeError("lambda is not named.")
|
||||||
|
|
||||||
def _get_paramlist_code(self):
|
|
||||||
return '(' + ''.join(param.get_code() for param in self.params).strip() + ')'
|
|
||||||
|
|
||||||
def _get_param_nodes(self):
|
def _get_param_nodes(self):
|
||||||
return self.children[1:-2]
|
return self.children[1:-2]
|
||||||
|
|
||||||
@@ -556,7 +547,6 @@ class Lambda(Function):
|
|||||||
"""
|
"""
|
||||||
Returns `None`, lambdas don't have annotations.
|
Returns `None`, lambdas don't have annotations.
|
||||||
"""
|
"""
|
||||||
# lambda functions do not support annotations
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -650,6 +640,10 @@ class WithStmt(Flow):
|
|||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def get_defined_names(self):
|
def get_defined_names(self):
|
||||||
|
"""
|
||||||
|
Returns the a list of `Name` that the with statement defines. The
|
||||||
|
defined names are set after `as`.
|
||||||
|
"""
|
||||||
names = []
|
names = []
|
||||||
for with_item in self.children[1:-2:2]:
|
for with_item in self.children[1:-2:2]:
|
||||||
# Check with items for 'as' names.
|
# Check with items for 'as' names.
|
||||||
@@ -669,13 +663,18 @@ class Import(PythonBaseNode):
|
|||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def get_path_for_name(self, name):
|
def get_path_for_name(self, name):
|
||||||
|
"""
|
||||||
|
The path is the list of names that leads to the searched name.
|
||||||
|
|
||||||
|
:return list of Name:
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
# The name may be an alias. If it is, just map it back to the name.
|
# The name may be an alias. If it is, just map it back to the name.
|
||||||
name = self.aliases()[name]
|
name = self._aliases()[name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
for path in self.paths():
|
for path in self.get_paths():
|
||||||
if name in path:
|
if name in path:
|
||||||
return path[:path.index(name) + 1]
|
return path[:path.index(name) + 1]
|
||||||
raise ValueError('Name should be defined in the import itself')
|
raise ValueError('Name should be defined in the import itself')
|
||||||
@@ -692,9 +691,14 @@ class ImportFrom(Import):
|
|||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def get_defined_names(self):
|
def get_defined_names(self):
|
||||||
|
"""
|
||||||
|
Returns the a list of `Name` that the import defines. The
|
||||||
|
defined names are set after `import` or in case an alias - `as` - is
|
||||||
|
present that name is returned.
|
||||||
|
"""
|
||||||
return [alias or name for name, alias in self._as_name_tuples()]
|
return [alias or name for name, alias in self._as_name_tuples()]
|
||||||
|
|
||||||
def aliases(self):
|
def _aliases(self):
|
||||||
"""Mapping from alias to its corresponding name."""
|
"""Mapping from alias to its corresponding name."""
|
||||||
return dict((alias, name) for name, alias in self._as_name_tuples()
|
return dict((alias, name) for name, alias in self._as_name_tuples()
|
||||||
if alias is not None)
|
if alias is not None)
|
||||||
@@ -738,16 +742,12 @@ class ImportFrom(Import):
|
|||||||
else:
|
else:
|
||||||
yield as_name.children[::2] # yields x, y -> ``x as y``
|
yield as_name.children[::2] # yields x, y -> ``x as y``
|
||||||
|
|
||||||
def star_import_name(self):
|
def get_paths(self):
|
||||||
"""
|
|
||||||
The last name defined in a star import.
|
|
||||||
"""
|
|
||||||
return self.paths()[-1][-1]
|
|
||||||
|
|
||||||
def paths(self):
|
|
||||||
"""
|
"""
|
||||||
The import paths defined in an import statement. Typically an array
|
The import paths defined in an import statement. Typically an array
|
||||||
like this: ``[<Name: datetime>, <Name: date>]``.
|
like this: ``[<Name: datetime>, <Name: date>]``.
|
||||||
|
|
||||||
|
:return list of list of Name:
|
||||||
"""
|
"""
|
||||||
dotted = self.get_from_names()
|
dotted = self.get_from_names()
|
||||||
|
|
||||||
@@ -762,6 +762,11 @@ class ImportName(Import):
|
|||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def get_defined_names(self):
|
def get_defined_names(self):
|
||||||
|
"""
|
||||||
|
Returns the a list of `Name` that the import defines. The defined names
|
||||||
|
is always the first name after `import` or in case an alias - `as` - is
|
||||||
|
present that name is returned.
|
||||||
|
"""
|
||||||
return [alias or path[0] for path, alias in self._dotted_as_names()]
|
return [alias or path[0] for path, alias in self._dotted_as_names()]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -769,7 +774,7 @@ class ImportName(Import):
|
|||||||
"""The level parameter of ``__import__``."""
|
"""The level parameter of ``__import__``."""
|
||||||
return 0 # Obviously 0 for imports without from.
|
return 0 # Obviously 0 for imports without from.
|
||||||
|
|
||||||
def paths(self):
|
def get_paths(self):
|
||||||
return [path for path, alias in self._dotted_as_names()]
|
return [path for path, alias in self._dotted_as_names()]
|
||||||
|
|
||||||
def _dotted_as_names(self):
|
def _dotted_as_names(self):
|
||||||
@@ -799,10 +804,13 @@ class ImportName(Import):
|
|||||||
|
|
||||||
import foo.bar
|
import foo.bar
|
||||||
"""
|
"""
|
||||||
return [1 for path, alias in self._dotted_as_names()
|
return bool([1 for path, alias in self._dotted_as_names()
|
||||||
if alias is None and len(path) > 1]
|
if alias is None and len(path) > 1])
|
||||||
|
|
||||||
def aliases(self):
|
def _aliases(self):
|
||||||
|
"""
|
||||||
|
:return list of Name: Returns all the alias
|
||||||
|
"""
|
||||||
return dict((alias, path[-1]) for path, alias in self._dotted_as_names()
|
return dict((alias, path[-1]) for path, alias in self._dotted_as_names()
|
||||||
if alias is not None)
|
if alias is not None)
|
||||||
|
|
||||||
@@ -880,14 +888,18 @@ class ExprStmt(PythonBaseNode, DocstringMixin):
|
|||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def get_defined_names(self):
|
def get_defined_names(self):
|
||||||
|
"""
|
||||||
|
Returns a list of `Name` defined before the `=` sign.
|
||||||
|
"""
|
||||||
names = []
|
names = []
|
||||||
if self.children[1].type == 'annassign':
|
if self.children[1].type == 'annassign':
|
||||||
names = _defined_names(self.children[0])
|
names = _defined_names(self.children[0])
|
||||||
return list(chain.from_iterable(
|
return [
|
||||||
_defined_names(self.children[i])
|
name
|
||||||
for i in range(0, len(self.children) - 2, 2)
|
for i in range(0, len(self.children) - 2, 2)
|
||||||
if '=' in self.children[i + 1].value)
|
if '=' in self.children[i + 1].value
|
||||||
) + names
|
for name in _defined_names(self.children[i])
|
||||||
|
] + names
|
||||||
|
|
||||||
def get_rhs(self):
|
def get_rhs(self):
|
||||||
"""Returns the right-hand-side of the equals."""
|
"""Returns the right-hand-side of the equals."""
|
||||||
@@ -925,6 +937,10 @@ class Param(PythonBaseNode):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def star_count(self):
|
def star_count(self):
|
||||||
|
"""
|
||||||
|
Is `0` in case of `foo`, `1` in case of `*foo` or `2` in case of
|
||||||
|
`**foo`.
|
||||||
|
"""
|
||||||
first = self.children[0]
|
first = self.children[0]
|
||||||
if first in ('*', '**'):
|
if first in ('*', '**'):
|
||||||
return len(first.value)
|
return len(first.value)
|
||||||
@@ -932,6 +948,10 @@ class Param(PythonBaseNode):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def default(self):
|
def default(self):
|
||||||
|
"""
|
||||||
|
The default is the test node that appears after the `=`. Is `None` in
|
||||||
|
case no default is present.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
return self.children[int(self.children[0] in ('*', '**')) + 2]
|
return self.children[int(self.children[0] in ('*', '**')) + 2]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@@ -939,6 +959,10 @@ class Param(PythonBaseNode):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def annotation(self):
|
def annotation(self):
|
||||||
|
"""
|
||||||
|
The default is the test node that appears after `->`. Is `None` in case
|
||||||
|
no annotation is present.
|
||||||
|
"""
|
||||||
tfpdef = self._tfpdef()
|
tfpdef = self._tfpdef()
|
||||||
if tfpdef.type == 'tfpdef':
|
if tfpdef.type == 'tfpdef':
|
||||||
assert tfpdef.children[1] == ":"
|
assert tfpdef.children[1] == ":"
|
||||||
@@ -957,6 +981,9 @@ class Param(PythonBaseNode):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
"""
|
||||||
|
The `Name` leaf of the param.
|
||||||
|
"""
|
||||||
if self._tfpdef().type == 'tfpdef':
|
if self._tfpdef().type == 'tfpdef':
|
||||||
return self._tfpdef().children[0]
|
return self._tfpdef().children[0]
|
||||||
else:
|
else:
|
||||||
@@ -965,7 +992,7 @@ class Param(PythonBaseNode):
|
|||||||
@property
|
@property
|
||||||
def position_index(self):
|
def position_index(self):
|
||||||
"""
|
"""
|
||||||
Returns the positional index of a paramter.
|
Property for the positional index of a paramter.
|
||||||
"""
|
"""
|
||||||
index = self.parent.children.index(self)
|
index = self.parent.children.index(self)
|
||||||
try:
|
try:
|
||||||
@@ -979,16 +1006,28 @@ class Param(PythonBaseNode):
|
|||||||
|
|
||||||
def get_parent_function(self):
|
def get_parent_function(self):
|
||||||
"""
|
"""
|
||||||
Returns the function/lambda a paramter is defined in.
|
Returns the function/lambda of a parameter.
|
||||||
"""
|
"""
|
||||||
return search_ancestor(self, ('funcdef', 'lambdef'))
|
return search_ancestor(self, 'funcdef', 'lambdef')
|
||||||
|
|
||||||
|
def get_code(self, normalized=False, include_prefix=True, include_comma=True):
|
||||||
|
"""
|
||||||
|
Like all the other get_code functions, but includes the param
|
||||||
|
`include_comma`.
|
||||||
|
|
||||||
|
:param include_comma bool: If enabled includes the comma in the string output.
|
||||||
|
"""
|
||||||
|
if include_comma:
|
||||||
|
return super(Param, self).get_code(normalized, include_prefix)
|
||||||
|
|
||||||
def get_description(self):
|
|
||||||
# TODO Remove?
|
|
||||||
children = self.children
|
children = self.children
|
||||||
if children[-1] == ',':
|
if children[-1] == ',':
|
||||||
children = children[:-1]
|
children = children[:-1]
|
||||||
return self._get_code_for_children(children, False, False)
|
return self._get_code_for_children(
|
||||||
|
children,
|
||||||
|
normalized=False,
|
||||||
|
include_prefix=include_prefix
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
default = '' if self.default is None else '=%s' % self.default.get_code()
|
default = '' if self.default is None else '=%s' % self.default.get_code()
|
||||||
@@ -999,8 +1038,8 @@ class CompFor(PythonBaseNode):
|
|||||||
type = 'comp_for'
|
type = 'comp_for'
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def is_scope(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_defined_names(self):
|
def get_defined_names(self):
|
||||||
|
"""
|
||||||
|
Returns the a list of `Name` that the comprehension defines.
|
||||||
|
"""
|
||||||
return _defined_names(self.children[1])
|
return _defined_names(self.children[1])
|
||||||
|
|||||||
@@ -2,21 +2,18 @@ from abc import abstractmethod, abstractproperty
|
|||||||
from jedi._compatibility import utf8_repr, encoding, is_py3
|
from jedi._compatibility import utf8_repr, encoding, is_py3
|
||||||
|
|
||||||
|
|
||||||
def search_ancestor(node, node_type_or_types):
|
def search_ancestor(node, *node_types):
|
||||||
"""
|
"""
|
||||||
Recursively looks at the parents of a node and checks if the type names
|
Recursively looks at the parents of a node and checks if the type names
|
||||||
match.
|
match.
|
||||||
|
|
||||||
:param node: The node that is looked at.
|
:param node: The node that is looked at.
|
||||||
:param node_type_or_types: A tuple or a string of type names that are
|
:param node_types: A tuple or a string of type names that are
|
||||||
searched for.
|
searched for.
|
||||||
"""
|
"""
|
||||||
if not isinstance(node_type_or_types, (list, tuple)):
|
|
||||||
node_type_or_types = (node_type_or_types,)
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
node = node.parent
|
node = node.parent
|
||||||
if node is None or node.type in node_type_or_types:
|
if node is None or node.type in node_types:
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -154,7 +154,12 @@ def get_call_signature(funcdef, width=72, call_string=None):
|
|||||||
call_string = '<lambda>'
|
call_string = '<lambda>'
|
||||||
else:
|
else:
|
||||||
call_string = funcdef.name.value
|
call_string = funcdef.name.value
|
||||||
code = call_string + funcdef._get_paramlist_code()
|
if funcdef.type == 'lambdef':
|
||||||
|
p = '(' + ''.join(param.get_code() for param in funcdef.params).strip() + ')'
|
||||||
|
else:
|
||||||
|
p = funcdef.children[2].get_code()
|
||||||
|
code = call_string + p
|
||||||
|
|
||||||
return '\n'.join(textwrap.wrap(code, width))
|
return '\n'.join(textwrap.wrap(code, width))
|
||||||
|
|
||||||
|
|
||||||
@@ -217,3 +222,21 @@ def get_following_comment_same_line(node):
|
|||||||
comment = comment[:comment.index("\n")]
|
comment = comment[:comment.index("\n")]
|
||||||
return comment
|
return comment
|
||||||
|
|
||||||
|
|
||||||
|
def is_scope(node):
|
||||||
|
return node.type in ('file_input', 'classdef', 'funcdef', 'lambdef', 'comp_for')
|
||||||
|
|
||||||
|
|
||||||
|
def get_parent_scope(node, include_flows=False):
|
||||||
|
"""
|
||||||
|
Returns the underlying scope.
|
||||||
|
"""
|
||||||
|
scope = node.parent
|
||||||
|
while scope is not None:
|
||||||
|
if include_flows and isinstance(scope, tree.Flow):
|
||||||
|
return scope
|
||||||
|
if is_scope(scope):
|
||||||
|
break
|
||||||
|
scope = scope.parent
|
||||||
|
return scope
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ else:
|
|||||||
'jedi')
|
'jedi')
|
||||||
cache_directory = os.path.expanduser(_cache_directory)
|
cache_directory = os.path.expanduser(_cache_directory)
|
||||||
"""
|
"""
|
||||||
The path where all the caches can be found.
|
The path where the cache is stored.
|
||||||
|
|
||||||
On Linux, this defaults to ``~/.cache/jedi/``, on OS X to
|
On Linux, this defaults to ``~/.cache/jedi/``, on OS X to
|
||||||
``~/Library/Caches/Jedi/`` and on Windows to ``%APPDATA%\\Jedi\\Jedi\\``.
|
``~/Library/Caches/Jedi/`` and on Windows to ``%APPDATA%\\Jedi\\Jedi\\``.
|
||||||
|
|||||||
@@ -20,7 +20,22 @@ class TestDocstring(unittest.TestCase):
|
|||||||
def func():
|
def func():
|
||||||
'''Docstring of `func`.'''
|
'''Docstring of `func`.'''
|
||||||
func""").goto_definitions()
|
func""").goto_definitions()
|
||||||
self.assertEqual(defs[0].raw_doc, 'Docstring of `func`.')
|
self.assertEqual(defs[0].docstring(), 'func()\n\nDocstring of `func`.')
|
||||||
|
|
||||||
|
def test_class_doc(self):
|
||||||
|
defs = jedi.Script("""
|
||||||
|
class TestClass():
|
||||||
|
'''Docstring of `TestClass`.'''
|
||||||
|
TestClass""").goto_definitions()
|
||||||
|
self.assertEqual(defs[0].docstring(), 'Docstring of `TestClass`.')
|
||||||
|
|
||||||
|
def test_instance_doc(self):
|
||||||
|
defs = jedi.Script("""
|
||||||
|
class TestClass():
|
||||||
|
'''Docstring of `TestClass`.'''
|
||||||
|
tc = TestClass()
|
||||||
|
tc""").goto_definitions()
|
||||||
|
self.assertEqual(defs[0].docstring(), 'Docstring of `TestClass`.')
|
||||||
|
|
||||||
@unittest.skip('need evaluator class for that')
|
@unittest.skip('need evaluator class for that')
|
||||||
def test_attribute_docstring(self):
|
def test_attribute_docstring(self):
|
||||||
@@ -28,7 +43,7 @@ class TestDocstring(unittest.TestCase):
|
|||||||
x = None
|
x = None
|
||||||
'''Docstring of `x`.'''
|
'''Docstring of `x`.'''
|
||||||
x""").goto_definitions()
|
x""").goto_definitions()
|
||||||
self.assertEqual(defs[0].raw_doc, 'Docstring of `x`.')
|
self.assertEqual(defs[0].docstring(), 'Docstring of `x`.')
|
||||||
|
|
||||||
@unittest.skip('need evaluator class for that')
|
@unittest.skip('need evaluator class for that')
|
||||||
def test_multiple_docstrings(self):
|
def test_multiple_docstrings(self):
|
||||||
@@ -38,7 +53,7 @@ class TestDocstring(unittest.TestCase):
|
|||||||
x = func
|
x = func
|
||||||
'''Docstring of `x`.'''
|
'''Docstring of `x`.'''
|
||||||
x""").goto_definitions()
|
x""").goto_definitions()
|
||||||
docs = [d.raw_doc for d in defs]
|
docs = [d.docstring() for d in defs]
|
||||||
self.assertEqual(docs, ['Original docstring.', 'Docstring of `x`.'])
|
self.assertEqual(docs, ['Original docstring.', 'Docstring of `x`.'])
|
||||||
|
|
||||||
def test_completion(self):
|
def test_completion(self):
|
||||||
@@ -105,6 +120,10 @@ class TestDocstring(unittest.TestCase):
|
|||||||
assert '__init__' in names
|
assert '__init__' in names
|
||||||
assert 'mro' not in names # Exists only for types.
|
assert 'mro' not in names # Exists only for types.
|
||||||
|
|
||||||
|
def test_docstring_keyword(self):
|
||||||
|
completions = jedi.Script('assert').completions()
|
||||||
|
self.assertIn('assert', completions[0].docstring())
|
||||||
|
|
||||||
@unittest.skipIf(numpydoc_unavailable, 'numpydoc module is unavailable')
|
@unittest.skipIf(numpydoc_unavailable, 'numpydoc module is unavailable')
|
||||||
def test_numpydoc_docstring(self):
|
def test_numpydoc_docstring(self):
|
||||||
s = dedent('''
|
s = dedent('''
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
"""
|
||||||
|
Tests of various import related things that could not be tested with "Black Box
|
||||||
|
Tests".
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -7,6 +12,9 @@ import jedi
|
|||||||
from jedi._compatibility import find_module_py33, find_module
|
from jedi._compatibility import find_module_py33, find_module
|
||||||
from ..helpers import cwd_at
|
from ..helpers import cwd_at
|
||||||
|
|
||||||
|
from jedi import Script
|
||||||
|
from jedi._compatibility import is_py26
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif('sys.version_info < (3,3)')
|
@pytest.mark.skipif('sys.version_info < (3,3)')
|
||||||
def test_find_module_py33():
|
def test_find_module_py33():
|
||||||
@@ -127,3 +135,85 @@ def test_import_completion_docstring():
|
|||||||
# However for performance reasons not all modules are loaded and the
|
# However for performance reasons not all modules are loaded and the
|
||||||
# docstring is empty in this case.
|
# docstring is empty in this case.
|
||||||
assert completions[0].docstring() == ''
|
assert completions[0].docstring() == ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_goto_definition_on_import():
|
||||||
|
assert Script("import sys_blabla", 1, 8).goto_definitions() == []
|
||||||
|
assert len(Script("import sys", 1, 8).goto_definitions()) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@cwd_at('jedi')
|
||||||
|
def test_complete_on_empty_import():
|
||||||
|
assert Script("from datetime import").completions()[0].name == 'import'
|
||||||
|
# should just list the files in the directory
|
||||||
|
assert 10 < len(Script("from .", path='whatever.py').completions()) < 30
|
||||||
|
|
||||||
|
# Global import
|
||||||
|
assert len(Script("from . import", 1, 5, 'whatever.py').completions()) > 30
|
||||||
|
# relative import
|
||||||
|
assert 10 < len(Script("from . import", 1, 6, 'whatever.py').completions()) < 30
|
||||||
|
|
||||||
|
# Global import
|
||||||
|
assert len(Script("from . import classes", 1, 5, 'whatever.py').completions()) > 30
|
||||||
|
# relative import
|
||||||
|
assert 10 < len(Script("from . import classes", 1, 6, 'whatever.py').completions()) < 30
|
||||||
|
|
||||||
|
wanted = set(['ImportError', 'import', 'ImportWarning'])
|
||||||
|
assert set([c.name for c in Script("import").completions()]) == wanted
|
||||||
|
if not is_py26: # python 2.6 doesn't always come with a library `import*`.
|
||||||
|
assert len(Script("import import", path='').completions()) > 0
|
||||||
|
|
||||||
|
# 111
|
||||||
|
assert Script("from datetime import").completions()[0].name == 'import'
|
||||||
|
assert Script("from datetime import ").completions()
|
||||||
|
|
||||||
|
|
||||||
|
def test_imports_on_global_namespace_without_path():
|
||||||
|
"""If the path is None, there shouldn't be any import problem"""
|
||||||
|
completions = Script("import operator").completions()
|
||||||
|
assert [c.name for c in completions] == ['operator']
|
||||||
|
completions = Script("import operator", path='example.py').completions()
|
||||||
|
assert [c.name for c in completions] == ['operator']
|
||||||
|
|
||||||
|
# the first one has a path the second doesn't
|
||||||
|
completions = Script("import keyword", path='example.py').completions()
|
||||||
|
assert [c.name for c in completions] == ['keyword']
|
||||||
|
completions = Script("import keyword").completions()
|
||||||
|
assert [c.name for c in completions] == ['keyword']
|
||||||
|
|
||||||
|
|
||||||
|
def test_named_import():
|
||||||
|
"""named import - jedi-vim issue #8"""
|
||||||
|
s = "import time as dt"
|
||||||
|
assert len(Script(s, 1, 15, '/').goto_definitions()) == 1
|
||||||
|
assert len(Script(s, 1, 10, '/').goto_definitions()) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif('True', reason='The nested import stuff is still very messy.')
|
||||||
|
def test_goto_following_on_imports():
|
||||||
|
s = "import multiprocessing.dummy; multiprocessing.dummy"
|
||||||
|
g = Script(s).goto_assignments()
|
||||||
|
assert len(g) == 1
|
||||||
|
assert (g[0].line, g[0].column) != (0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_after_from():
|
||||||
|
def check(source, result, column=None):
|
||||||
|
completions = Script(source, column=column).completions()
|
||||||
|
assert [c.name for c in completions] == result
|
||||||
|
|
||||||
|
check('\nfrom os. ', ['path'])
|
||||||
|
check('\nfrom os ', ['import'])
|
||||||
|
check('from os ', ['import'])
|
||||||
|
check('\nfrom os import whatever', ['import'], len('from os im'))
|
||||||
|
|
||||||
|
check('from os\\\n', ['import'])
|
||||||
|
check('from os \\\n', ['import'])
|
||||||
|
|
||||||
|
|
||||||
|
def test_path_issues():
|
||||||
|
"""
|
||||||
|
See pull request #684 for details.
|
||||||
|
"""
|
||||||
|
source = '''from datetime import '''
|
||||||
|
assert jedi.Script(source).completions()
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
"""
|
|
||||||
Tests of various import related things that could not be tested with "Black Box
|
|
||||||
Tests".
|
|
||||||
"""
|
|
||||||
from jedi import Script
|
|
||||||
from .helpers import cwd_at
|
|
||||||
from jedi._compatibility import is_py26
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
def test_goto_definition_on_import():
|
|
||||||
assert Script("import sys_blabla", 1, 8).goto_definitions() == []
|
|
||||||
assert len(Script("import sys", 1, 8).goto_definitions()) == 1
|
|
||||||
|
|
||||||
|
|
||||||
@cwd_at('jedi')
|
|
||||||
def test_complete_on_empty_import():
|
|
||||||
assert Script("from datetime import").completions()[0].name == 'import'
|
|
||||||
# should just list the files in the directory
|
|
||||||
assert 10 < len(Script("from .", path='whatever.py').completions()) < 30
|
|
||||||
|
|
||||||
# Global import
|
|
||||||
assert len(Script("from . import", 1, 5, 'whatever.py').completions()) > 30
|
|
||||||
# relative import
|
|
||||||
assert 10 < len(Script("from . import", 1, 6, 'whatever.py').completions()) < 30
|
|
||||||
|
|
||||||
# Global import
|
|
||||||
assert len(Script("from . import classes", 1, 5, 'whatever.py').completions()) > 30
|
|
||||||
# relative import
|
|
||||||
assert 10 < len(Script("from . import classes", 1, 6, 'whatever.py').completions()) < 30
|
|
||||||
|
|
||||||
wanted = set(['ImportError', 'import', 'ImportWarning'])
|
|
||||||
assert set([c.name for c in Script("import").completions()]) == wanted
|
|
||||||
if not is_py26: # python 2.6 doesn't always come with a library `import*`.
|
|
||||||
assert len(Script("import import", path='').completions()) > 0
|
|
||||||
|
|
||||||
# 111
|
|
||||||
assert Script("from datetime import").completions()[0].name == 'import'
|
|
||||||
assert Script("from datetime import ").completions()
|
|
||||||
|
|
||||||
|
|
||||||
def test_imports_on_global_namespace_without_path():
|
|
||||||
"""If the path is None, there shouldn't be any import problem"""
|
|
||||||
completions = Script("import operator").completions()
|
|
||||||
assert [c.name for c in completions] == ['operator']
|
|
||||||
completions = Script("import operator", path='example.py').completions()
|
|
||||||
assert [c.name for c in completions] == ['operator']
|
|
||||||
|
|
||||||
# the first one has a path the second doesn't
|
|
||||||
completions = Script("import keyword", path='example.py').completions()
|
|
||||||
assert [c.name for c in completions] == ['keyword']
|
|
||||||
completions = Script("import keyword").completions()
|
|
||||||
assert [c.name for c in completions] == ['keyword']
|
|
||||||
|
|
||||||
|
|
||||||
def test_named_import():
|
|
||||||
"""named import - jedi-vim issue #8"""
|
|
||||||
s = "import time as dt"
|
|
||||||
assert len(Script(s, 1, 15, '/').goto_definitions()) == 1
|
|
||||||
assert len(Script(s, 1, 10, '/').goto_definitions()) == 1
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif('True', reason='The nested import stuff is still very messy.')
|
|
||||||
def test_goto_following_on_imports():
|
|
||||||
s = "import multiprocessing.dummy; multiprocessing.dummy"
|
|
||||||
g = Script(s).goto_assignments()
|
|
||||||
assert len(g) == 1
|
|
||||||
assert (g[0].line, g[0].column) != (0, 0)
|
|
||||||
|
|
||||||
|
|
||||||
def test_after_from():
|
|
||||||
def check(source, result, column=None):
|
|
||||||
completions = Script(source, column=column).completions()
|
|
||||||
assert [c.name for c in completions] == result
|
|
||||||
|
|
||||||
check('\nfrom os. ', ['path'])
|
|
||||||
check('\nfrom os ', ['import'])
|
|
||||||
check('from os ', ['import'])
|
|
||||||
check('\nfrom os import whatever', ['import'], len('from os im'))
|
|
||||||
|
|
||||||
check('from os\\\n', ['import'])
|
|
||||||
check('from os \\\n', ['import'])
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
"""
|
|
||||||
Test the Jedi "System" which means for example to test if imports are
|
|
||||||
correctly used.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
import jedi
|
|
||||||
|
|
||||||
|
|
||||||
def test_settings_module():
|
|
||||||
"""
|
|
||||||
jedi.settings and jedi.cache.settings must be the same module.
|
|
||||||
"""
|
|
||||||
from jedi import cache
|
|
||||||
from jedi import settings
|
|
||||||
assert cache.settings is settings
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_duplicate_modules():
|
|
||||||
"""
|
|
||||||
Make sure that import hack works as expected.
|
|
||||||
|
|
||||||
Jedi does an import hack (see: jedi/__init__.py) to have submodules
|
|
||||||
with circular dependencies. The modules in this circular dependency
|
|
||||||
"loop" must be imported by ``import <module>`` rather than normal
|
|
||||||
``from jedi import <module>`` (or ``from . jedi ...``). This test
|
|
||||||
make sure that this is satisfied.
|
|
||||||
|
|
||||||
See also:
|
|
||||||
|
|
||||||
- `#160 <https://github.com/davidhalter/jedi/issues/160>`_
|
|
||||||
- `#161 <https://github.com/davidhalter/jedi/issues/161>`_
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
jedipath = os.path.dirname(os.path.abspath(jedi.__file__))
|
|
||||||
|
|
||||||
def is_submodule(m):
|
|
||||||
try:
|
|
||||||
filepath = m.__file__
|
|
||||||
except AttributeError:
|
|
||||||
return False
|
|
||||||
return os.path.abspath(filepath).startswith(jedipath)
|
|
||||||
|
|
||||||
modules = list(filter(is_submodule, sys.modules.values()))
|
|
||||||
top_modules = [m for m in modules if not m.__name__.startswith('jedi.')]
|
|
||||||
for m in modules:
|
|
||||||
if m is jedi:
|
|
||||||
# py.test automatically improts `jedi.*` when --doctest-modules
|
|
||||||
# is given. So this test cannot succeeds.
|
|
||||||
continue
|
|
||||||
for tm in top_modules:
|
|
||||||
try:
|
|
||||||
imported = getattr(m, tm.__name__)
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
|
||||||
if inspect.ismodule(imported):
|
|
||||||
# module could have a function with the same name, e.g.
|
|
||||||
# `keywords.keywords`.
|
|
||||||
assert imported is tm
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from jedi._compatibility import u
|
|
||||||
from jedi.parser.python import parse
|
|
||||||
|
|
||||||
|
|
||||||
def test_basic_parsing():
|
|
||||||
def compare(string):
|
|
||||||
"""Generates the AST object and then regenerates the code."""
|
|
||||||
assert parse(string).get_code() == string
|
|
||||||
|
|
||||||
compare(u('\na #pass\n'))
|
|
||||||
compare(u('wblabla* 1\t\n'))
|
|
||||||
compare(u('def x(a, b:3): pass\n'))
|
|
||||||
compare(u('assert foo\n'))
|
|
||||||
@@ -2,7 +2,6 @@ from textwrap import dedent
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import jedi
|
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.common import splitlines
|
from jedi.common import splitlines
|
||||||
from jedi import cache
|
from jedi import cache
|
||||||
@@ -386,38 +385,6 @@ def test_node_insertion(differ):
|
|||||||
differ.parse(code2, parsers=1, copies=2)
|
differ.parse(code2, parsers=1, copies=2)
|
||||||
|
|
||||||
|
|
||||||
def test_add_to_end():
|
|
||||||
"""
|
|
||||||
fast_parser doesn't parse everything again. It just updates with the
|
|
||||||
help of caches, this is an example that didn't work.
|
|
||||||
"""
|
|
||||||
|
|
||||||
a = dedent("""\
|
|
||||||
class Abc():
|
|
||||||
def abc(self):
|
|
||||||
self.x = 3
|
|
||||||
|
|
||||||
class Two(Abc):
|
|
||||||
def g(self):
|
|
||||||
self
|
|
||||||
""") # ^ here is the first completion
|
|
||||||
|
|
||||||
b = " def h(self):\n" \
|
|
||||||
" self."
|
|
||||||
|
|
||||||
def complete(code, line=None, column=None):
|
|
||||||
script = jedi.Script(code, line, column, 'example.py')
|
|
||||||
assert script.completions()
|
|
||||||
_assert_valid_graph(script._get_module())
|
|
||||||
|
|
||||||
complete(a, 7, 12)
|
|
||||||
complete(a + b)
|
|
||||||
|
|
||||||
a = a[:-1] + '.\n'
|
|
||||||
complete(a, 7, 13)
|
|
||||||
complete(a + b)
|
|
||||||
|
|
||||||
|
|
||||||
def test_whitespace_at_end(differ):
|
def test_whitespace_at_end(differ):
|
||||||
code = dedent('str\n\n')
|
code = dedent('str\n\n')
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,11 @@ However the tests might still be relevant for the parser.
|
|||||||
|
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
import jedi
|
|
||||||
from jedi._compatibility import u
|
|
||||||
from jedi.parser.python import parse
|
from jedi.parser.python import parse
|
||||||
|
|
||||||
|
|
||||||
def test_carriage_return_splitting():
|
def test_carriage_return_splitting():
|
||||||
source = u(dedent('''
|
source = dedent('''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -22,50 +20,18 @@ def test_carriage_return_splitting():
|
|||||||
|
|
||||||
class Foo():
|
class Foo():
|
||||||
pass
|
pass
|
||||||
'''))
|
''')
|
||||||
source = source.replace('\n', '\r\n')
|
source = source.replace('\n', '\r\n')
|
||||||
module = parse(source)
|
module = parse(source)
|
||||||
assert [n.value for lst in module.get_used_names().values() for n in lst] == ['Foo']
|
assert [n.value for lst in module.get_used_names().values() for n in lst] == ['Foo']
|
||||||
|
|
||||||
|
|
||||||
def test_class_in_docstr():
|
def check_p(src):
|
||||||
"""
|
|
||||||
Regression test for a problem with classes in docstrings.
|
|
||||||
"""
|
|
||||||
a = '"\nclasses\n"'
|
|
||||||
jedi.Script(a, 1, 0)._get_module()
|
|
||||||
|
|
||||||
b = a + '\nimport os'
|
|
||||||
assert jedi.Script(b, 4, 8).goto_assignments()
|
|
||||||
|
|
||||||
|
|
||||||
def check_p(src, number_parsers_used, number_of_splits=None, number_of_misses=0):
|
|
||||||
if number_of_splits is None:
|
|
||||||
number_of_splits = number_parsers_used
|
|
||||||
|
|
||||||
module_node = parse(src)
|
module_node = parse(src)
|
||||||
|
|
||||||
assert src == module_node.get_code()
|
assert src == module_node.get_code()
|
||||||
return module_node
|
return module_node
|
||||||
|
|
||||||
|
|
||||||
def test_if():
|
|
||||||
src = dedent('''\
|
|
||||||
def func():
|
|
||||||
x = 3
|
|
||||||
if x:
|
|
||||||
def y():
|
|
||||||
return x
|
|
||||||
return y()
|
|
||||||
|
|
||||||
func()
|
|
||||||
''')
|
|
||||||
|
|
||||||
# Two parsers needed, one for pass and one for the function.
|
|
||||||
check_p(src, 2)
|
|
||||||
assert [d.name for d in jedi.Script(src, 8, 6).goto_definitions()] == ['int']
|
|
||||||
|
|
||||||
|
|
||||||
def test_for():
|
def test_for():
|
||||||
src = dedent("""\
|
src = dedent("""\
|
||||||
for a in [1,2]:
|
for a in [1,2]:
|
||||||
@@ -74,7 +40,7 @@ def test_for():
|
|||||||
for a1 in 1,"":
|
for a1 in 1,"":
|
||||||
a1
|
a1
|
||||||
""")
|
""")
|
||||||
check_p(src, 1)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_class_with_class_var():
|
def test_class_with_class_var():
|
||||||
@@ -85,7 +51,7 @@ def test_class_with_class_var():
|
|||||||
self.foo = 4
|
self.foo = 4
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
check_p(src, 3)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_func_with_if():
|
def test_func_with_if():
|
||||||
@@ -99,7 +65,7 @@ def test_func_with_if():
|
|||||||
else:
|
else:
|
||||||
return a
|
return a
|
||||||
""")
|
""")
|
||||||
check_p(src, 1)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_decorator():
|
def test_decorator():
|
||||||
@@ -109,7 +75,7 @@ def test_decorator():
|
|||||||
def dec(self, a):
|
def dec(self, a):
|
||||||
return a
|
return a
|
||||||
""")
|
""")
|
||||||
check_p(src, 2)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_nested_funcs():
|
def test_nested_funcs():
|
||||||
@@ -119,25 +85,7 @@ def test_nested_funcs():
|
|||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
""")
|
""")
|
||||||
check_p(src, 3)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_class_and_if():
|
|
||||||
src = dedent("""\
|
|
||||||
class V:
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if 1:
|
|
||||||
c = 3
|
|
||||||
|
|
||||||
def a_func():
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# COMMENT
|
|
||||||
a_func()""")
|
|
||||||
check_p(src, 5, 5)
|
|
||||||
assert [d.name for d in jedi.Script(src).goto_definitions()] == ['int']
|
|
||||||
|
|
||||||
|
|
||||||
def test_multi_line_params():
|
def test_multi_line_params():
|
||||||
@@ -148,7 +96,7 @@ def test_multi_line_params():
|
|||||||
|
|
||||||
foo = 1
|
foo = 1
|
||||||
""")
|
""")
|
||||||
check_p(src, 2)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_class_func_if():
|
def test_class_func_if():
|
||||||
@@ -162,7 +110,7 @@ def test_class_func_if():
|
|||||||
|
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
check_p(src, 3)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_multi_line_for():
|
def test_multi_line_for():
|
||||||
@@ -173,7 +121,7 @@ def test_multi_line_for():
|
|||||||
|
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
check_p(src, 1)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_wrong_indentation():
|
def test_wrong_indentation():
|
||||||
@@ -183,7 +131,7 @@ def test_wrong_indentation():
|
|||||||
b
|
b
|
||||||
a
|
a
|
||||||
""")
|
""")
|
||||||
#check_p(src, 1)
|
check_p(src)
|
||||||
|
|
||||||
src = dedent("""\
|
src = dedent("""\
|
||||||
def complex():
|
def complex():
|
||||||
@@ -195,7 +143,7 @@ def test_wrong_indentation():
|
|||||||
def other():
|
def other():
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
check_p(src, 3)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_strange_parentheses():
|
def test_strange_parentheses():
|
||||||
@@ -206,7 +154,7 @@ def test_strange_parentheses():
|
|||||||
def x():
|
def x():
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
check_p(src, 2)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_fake_parentheses():
|
def test_fake_parentheses():
|
||||||
@@ -224,7 +172,7 @@ def test_fake_parentheses():
|
|||||||
def z():
|
def z():
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
check_p(src, 3, 2, 1)
|
check_p(src)
|
||||||
|
|
||||||
|
|
||||||
def test_additional_indent():
|
def test_additional_indent():
|
||||||
@@ -234,45 +182,7 @@ def test_additional_indent():
|
|||||||
pass
|
pass
|
||||||
''')
|
''')
|
||||||
|
|
||||||
check_p(source, 2)
|
check_p(source)
|
||||||
|
|
||||||
|
|
||||||
def test_incomplete_function():
|
|
||||||
source = '''return ImportErr'''
|
|
||||||
|
|
||||||
script = jedi.Script(dedent(source), 1, 3)
|
|
||||||
assert script.completions()
|
|
||||||
|
|
||||||
|
|
||||||
def test_string_literals():
|
|
||||||
"""Simplified case of jedi-vim#377."""
|
|
||||||
source = dedent("""
|
|
||||||
x = ur'''
|
|
||||||
|
|
||||||
def foo():
|
|
||||||
pass
|
|
||||||
""")
|
|
||||||
|
|
||||||
script = jedi.Script(dedent(source))
|
|
||||||
assert script._get_module().tree_node.end_pos == (6, 0)
|
|
||||||
assert script.completions()
|
|
||||||
|
|
||||||
|
|
||||||
def test_decorator_string_issue():
|
|
||||||
"""
|
|
||||||
Test case from #589
|
|
||||||
"""
|
|
||||||
source = dedent('''\
|
|
||||||
"""
|
|
||||||
@"""
|
|
||||||
def bla():
|
|
||||||
pass
|
|
||||||
|
|
||||||
bla.''')
|
|
||||||
|
|
||||||
s = jedi.Script(source)
|
|
||||||
assert s.completions()
|
|
||||||
assert s._get_module().tree_node.get_code() == source
|
|
||||||
|
|
||||||
|
|
||||||
def test_round_trip():
|
def test_round_trip():
|
||||||
@@ -292,4 +202,4 @@ def test_parentheses_in_string():
|
|||||||
import abc
|
import abc
|
||||||
|
|
||||||
abc.''')
|
abc.''')
|
||||||
check_p(code, 2, 1, 1)
|
check_p(code)
|
||||||
|
|||||||
@@ -4,61 +4,21 @@ from textwrap import dedent
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import jedi
|
|
||||||
from jedi._compatibility import u, is_py3
|
from jedi._compatibility import u, is_py3
|
||||||
from jedi.parser.python import parse, load_grammar
|
from jedi.parser.python import parse, load_grammar
|
||||||
from jedi.parser.python import tree
|
from jedi.parser.python import tree
|
||||||
from jedi.common import splitlines
|
from jedi.common import splitlines
|
||||||
from jedi.parser_utils import get_statement_of_position, \
|
|
||||||
clean_scope_docstring, safe_literal_eval
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_statement_on_import():
|
def test_basic_parsing():
|
||||||
"""github #285"""
|
def compare(string):
|
||||||
s = "from datetime import (\n" \
|
"""Generates the AST object and then regenerates the code."""
|
||||||
" time)"
|
assert parse(string).get_code() == string
|
||||||
|
|
||||||
for pos in [(2, 1), (2, 4)]:
|
compare('\na #pass\n')
|
||||||
p = parse(s)
|
compare('wblabla* 1\t\n')
|
||||||
stmt = get_statement_of_position(p, pos)
|
compare('def x(a, b:3): pass\n')
|
||||||
assert isinstance(stmt, tree.Import)
|
compare('assert foo\n')
|
||||||
assert [n.value for n in stmt.get_defined_names()] == ['time']
|
|
||||||
|
|
||||||
|
|
||||||
class TestCallAndName():
|
|
||||||
def get_call(self, source):
|
|
||||||
# Get the simple_stmt and then the first one.
|
|
||||||
simple_stmt = parse(source).children[0]
|
|
||||||
return simple_stmt.children[0]
|
|
||||||
|
|
||||||
def test_name_and_call_positions(self):
|
|
||||||
name = self.get_call('name\nsomething_else')
|
|
||||||
assert name.value == 'name'
|
|
||||||
assert name.start_pos == (1, 0)
|
|
||||||
assert name.end_pos == (1, 4)
|
|
||||||
|
|
||||||
leaf = self.get_call('1.0\n')
|
|
||||||
assert leaf.value == '1.0'
|
|
||||||
assert safe_literal_eval(leaf.value) == 1.0
|
|
||||||
assert leaf.start_pos == (1, 0)
|
|
||||||
assert leaf.end_pos == (1, 3)
|
|
||||||
|
|
||||||
def test_call_type(self):
|
|
||||||
call = self.get_call('hello')
|
|
||||||
assert isinstance(call, tree.Name)
|
|
||||||
|
|
||||||
def test_literal_type(self):
|
|
||||||
literal = self.get_call('1.0')
|
|
||||||
assert isinstance(literal, tree.Literal)
|
|
||||||
assert type(safe_literal_eval(literal.value)) == float
|
|
||||||
|
|
||||||
literal = self.get_call('1')
|
|
||||||
assert isinstance(literal, tree.Literal)
|
|
||||||
assert type(safe_literal_eval(literal.value)) == int
|
|
||||||
|
|
||||||
literal = self.get_call('"hello"')
|
|
||||||
assert isinstance(literal, tree.Literal)
|
|
||||||
assert safe_literal_eval(literal.value) == 'hello'
|
|
||||||
|
|
||||||
|
|
||||||
class TestSubscopes():
|
class TestSubscopes():
|
||||||
@@ -125,33 +85,6 @@ def test_incomplete_list_comprehension():
|
|||||||
['error_node', 'error_node', 'newline', 'endmarker']
|
['error_node', 'error_node', 'newline', 'endmarker']
|
||||||
|
|
||||||
|
|
||||||
def test_hex_values_in_docstring():
|
|
||||||
source = r'''
|
|
||||||
def foo(object):
|
|
||||||
"""
|
|
||||||
\xff
|
|
||||||
"""
|
|
||||||
return 1
|
|
||||||
'''
|
|
||||||
|
|
||||||
doc = clean_scope_docstring(next(parse(source).iter_funcdefs()))
|
|
||||||
if is_py3:
|
|
||||||
assert doc == '\xff'
|
|
||||||
else:
|
|
||||||
assert doc == u('<EFBFBD>')
|
|
||||||
|
|
||||||
|
|
||||||
def test_error_correction_with():
|
|
||||||
source = """
|
|
||||||
with open() as f:
|
|
||||||
try:
|
|
||||||
f."""
|
|
||||||
comps = jedi.Script(source).completions()
|
|
||||||
assert len(comps) > 30
|
|
||||||
# `open` completions have a closed attribute.
|
|
||||||
assert [1 for c in comps if c.name == 'closed']
|
|
||||||
|
|
||||||
|
|
||||||
def test_newline_positions():
|
def test_newline_positions():
|
||||||
endmarker = parse('a\n').children[-1]
|
endmarker = parse('a\n').children[-1]
|
||||||
assert endmarker.end_pos == (2, 0)
|
assert endmarker.end_pos == (2, 0)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import pytest
|
|||||||
|
|
||||||
from jedi.parser.python import parse
|
from jedi.parser.python import parse
|
||||||
from jedi.parser.python import tree
|
from jedi.parser.python import tree
|
||||||
from jedi.parser_utils import get_doc_with_call_signature, get_call_signature
|
|
||||||
|
|
||||||
|
|
||||||
class TestsFunctionAndLambdaParsing(object):
|
class TestsFunctionAndLambdaParsing(object):
|
||||||
@@ -14,13 +13,11 @@ class TestsFunctionAndLambdaParsing(object):
|
|||||||
FIXTURES = [
|
FIXTURES = [
|
||||||
('def my_function(x, y, z) -> str:\n return x + y * z\n', {
|
('def my_function(x, y, z) -> str:\n return x + y * z\n', {
|
||||||
'name': 'my_function',
|
'name': 'my_function',
|
||||||
'call_sig': 'my_function(x, y, z)',
|
|
||||||
'params': ['x', 'y', 'z'],
|
'params': ['x', 'y', 'z'],
|
||||||
'annotation': "str",
|
'annotation': "str",
|
||||||
}),
|
}),
|
||||||
('lambda x, y, z: x + y * z\n', {
|
('lambda x, y, z: x + y * z\n', {
|
||||||
'name': '<lambda>',
|
'name': '<lambda>',
|
||||||
'call_sig': '<lambda>(x, y, z)',
|
|
||||||
'params': ['x', 'y', 'z'],
|
'params': ['x', 'y', 'z'],
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
@@ -62,9 +59,3 @@ class TestsFunctionAndLambdaParsing(object):
|
|||||||
assert node.annotation is None
|
assert node.annotation is None
|
||||||
else:
|
else:
|
||||||
assert node.annotation.value == expected_annotation
|
assert node.annotation.value == expected_annotation
|
||||||
|
|
||||||
def test_get_call_signature(self, node, expected):
|
|
||||||
assert get_call_signature(node) == expected['call_sig']
|
|
||||||
|
|
||||||
def test_doc(self, node, expected):
|
|
||||||
assert get_doc_with_call_signature(node) == (expected['call_sig'] + '\n\n')
|
|
||||||
|
|||||||
@@ -147,12 +147,6 @@ class TokenTest(unittest.TestCase):
|
|||||||
assert safe_literal_eval(string_tok.value) == 'test'
|
assert safe_literal_eval(string_tok.value) == 'test'
|
||||||
|
|
||||||
|
|
||||||
def test_tokenizer_with_string_literal_backslash():
|
|
||||||
import jedi
|
|
||||||
c = jedi.Script("statement = u'foo\\\n'; statement").goto_definitions()
|
|
||||||
assert c[0]._name._context.obj == 'foo'
|
|
||||||
|
|
||||||
|
|
||||||
def test_ur_literals():
|
def test_ur_literals():
|
||||||
"""
|
"""
|
||||||
Decided to parse `u''` literals regardless of Python version. This makes
|
Decided to parse `u''` literals regardless of Python version. This makes
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import jedi
|
|
||||||
|
|
||||||
|
|
||||||
def test_form_feed_characters():
|
|
||||||
s = "\f\nclass Test(object):\n pass"
|
|
||||||
jedi.Script(s, line=2, column=18).call_signatures()
|
|
||||||
87
test/test_parso_integration/test_basic.py
Normal file
87
test/test_parso_integration/test_basic.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
from jedi.parser.python import parse
|
||||||
|
import jedi
|
||||||
|
|
||||||
|
|
||||||
|
def test_form_feed_characters():
|
||||||
|
s = "\f\nclass Test(object):\n pass"
|
||||||
|
jedi.Script(s, line=2, column=18).call_signatures()
|
||||||
|
|
||||||
|
|
||||||
|
def check_p(src):
|
||||||
|
module_node = parse(src)
|
||||||
|
assert src == module_node.get_code()
|
||||||
|
return module_node
|
||||||
|
|
||||||
|
|
||||||
|
def test_if():
|
||||||
|
src = dedent('''\
|
||||||
|
def func():
|
||||||
|
x = 3
|
||||||
|
if x:
|
||||||
|
def y():
|
||||||
|
return x
|
||||||
|
return y()
|
||||||
|
|
||||||
|
func()
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Two parsers needed, one for pass and one for the function.
|
||||||
|
check_p(src)
|
||||||
|
assert [d.name for d in jedi.Script(src, 8, 6).goto_definitions()] == ['int']
|
||||||
|
|
||||||
|
|
||||||
|
def test_class_and_if():
|
||||||
|
src = dedent("""\
|
||||||
|
class V:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if 1:
|
||||||
|
c = 3
|
||||||
|
|
||||||
|
def a_func():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# COMMENT
|
||||||
|
a_func()""")
|
||||||
|
check_p(src)
|
||||||
|
assert [d.name for d in jedi.Script(src).goto_definitions()] == ['int']
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_to_end():
|
||||||
|
"""
|
||||||
|
The diff parser doesn't parse everything again. It just updates with the
|
||||||
|
help of caches, this is an example that didn't work.
|
||||||
|
"""
|
||||||
|
|
||||||
|
a = dedent("""\
|
||||||
|
class Abc():
|
||||||
|
def abc(self):
|
||||||
|
self.x = 3
|
||||||
|
|
||||||
|
class Two(Abc):
|
||||||
|
def g(self):
|
||||||
|
self
|
||||||
|
""") # ^ here is the first completion
|
||||||
|
|
||||||
|
b = " def h(self):\n" \
|
||||||
|
" self."
|
||||||
|
|
||||||
|
def complete(code, line=None, column=None):
|
||||||
|
script = jedi.Script(code, line, column, 'example.py')
|
||||||
|
assert script.completions()
|
||||||
|
|
||||||
|
complete(a, 7, 12)
|
||||||
|
complete(a + b)
|
||||||
|
|
||||||
|
a = a[:-1] + '.\n'
|
||||||
|
complete(a, 7, 13)
|
||||||
|
complete(a + b)
|
||||||
|
|
||||||
|
|
||||||
|
def test_tokenizer_with_string_literal_backslash():
|
||||||
|
import jedi
|
||||||
|
c = jedi.Script("statement = u'foo\\\n'; statement").goto_definitions()
|
||||||
|
assert c[0]._name._context.obj == 'foo'
|
||||||
52
test/test_parso_integration/test_error_correction.py
Normal file
52
test/test_parso_integration/test_error_correction.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
import jedi
|
||||||
|
|
||||||
|
|
||||||
|
def test_error_correction_with():
|
||||||
|
source = """
|
||||||
|
with open() as f:
|
||||||
|
try:
|
||||||
|
f."""
|
||||||
|
comps = jedi.Script(source).completions()
|
||||||
|
assert len(comps) > 30
|
||||||
|
# `open` completions have a closed attribute.
|
||||||
|
assert [1 for c in comps if c.name == 'closed']
|
||||||
|
|
||||||
|
|
||||||
|
def test_string_literals():
|
||||||
|
"""Simplified case of jedi-vim#377."""
|
||||||
|
source = dedent("""
|
||||||
|
x = ur'''
|
||||||
|
|
||||||
|
def foo():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
|
||||||
|
script = jedi.Script(dedent(source))
|
||||||
|
assert script._get_module().tree_node.end_pos == (6, 0)
|
||||||
|
assert script.completions()
|
||||||
|
|
||||||
|
|
||||||
|
def test_incomplete_function():
|
||||||
|
source = '''return ImportErr'''
|
||||||
|
|
||||||
|
script = jedi.Script(dedent(source), 1, 3)
|
||||||
|
assert script.completions()
|
||||||
|
|
||||||
|
|
||||||
|
def test_decorator_string_issue():
|
||||||
|
"""
|
||||||
|
Test case from #589
|
||||||
|
"""
|
||||||
|
source = dedent('''\
|
||||||
|
"""
|
||||||
|
@"""
|
||||||
|
def bla():
|
||||||
|
pass
|
||||||
|
|
||||||
|
bla.''')
|
||||||
|
|
||||||
|
s = jedi.Script(source)
|
||||||
|
assert s.completions()
|
||||||
|
assert s._get_module().tree_node.get_code() == source
|
||||||
84
test/test_parso_integration/test_parser_utils.py
Normal file
84
test/test_parso_integration/test_parser_utils.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
from jedi._compatibility import u, is_py3
|
||||||
|
from jedi import parser_utils
|
||||||
|
from jedi.parser.python import parse
|
||||||
|
from jedi.parser.python import tree
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
class TestCallAndName():
|
||||||
|
def get_call(self, source):
|
||||||
|
# Get the simple_stmt and then the first one.
|
||||||
|
simple_stmt = parse(source).children[0]
|
||||||
|
return simple_stmt.children[0]
|
||||||
|
|
||||||
|
def test_name_and_call_positions(self):
|
||||||
|
name = self.get_call('name\nsomething_else')
|
||||||
|
assert name.value == 'name'
|
||||||
|
assert name.start_pos == (1, 0)
|
||||||
|
assert name.end_pos == (1, 4)
|
||||||
|
|
||||||
|
leaf = self.get_call('1.0\n')
|
||||||
|
assert leaf.value == '1.0'
|
||||||
|
assert parser_utils.safe_literal_eval(leaf.value) == 1.0
|
||||||
|
assert leaf.start_pos == (1, 0)
|
||||||
|
assert leaf.end_pos == (1, 3)
|
||||||
|
|
||||||
|
def test_call_type(self):
|
||||||
|
call = self.get_call('hello')
|
||||||
|
assert isinstance(call, tree.Name)
|
||||||
|
|
||||||
|
def test_literal_type(self):
|
||||||
|
literal = self.get_call('1.0')
|
||||||
|
assert isinstance(literal, tree.Literal)
|
||||||
|
assert type(parser_utils.safe_literal_eval(literal.value)) == float
|
||||||
|
|
||||||
|
literal = self.get_call('1')
|
||||||
|
assert isinstance(literal, tree.Literal)
|
||||||
|
assert type(parser_utils.safe_literal_eval(literal.value)) == int
|
||||||
|
|
||||||
|
literal = self.get_call('"hello"')
|
||||||
|
assert isinstance(literal, tree.Literal)
|
||||||
|
assert parser_utils.safe_literal_eval(literal.value) == 'hello'
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_statement_on_import():
|
||||||
|
"""github #285"""
|
||||||
|
s = "from datetime import (\n" \
|
||||||
|
" time)"
|
||||||
|
|
||||||
|
for pos in [(2, 1), (2, 4)]:
|
||||||
|
p = parse(s)
|
||||||
|
stmt = parser_utils.get_statement_of_position(p, pos)
|
||||||
|
assert isinstance(stmt, tree.Import)
|
||||||
|
assert [n.value for n in stmt.get_defined_names()] == ['time']
|
||||||
|
|
||||||
|
|
||||||
|
def test_hex_values_in_docstring():
|
||||||
|
source = r'''
|
||||||
|
def foo(object):
|
||||||
|
"""
|
||||||
|
\xff
|
||||||
|
"""
|
||||||
|
return 1
|
||||||
|
'''
|
||||||
|
|
||||||
|
doc = parser_utils.clean_scope_docstring(next(parse(source).iter_funcdefs()))
|
||||||
|
if is_py3:
|
||||||
|
assert doc == '\xff'
|
||||||
|
else:
|
||||||
|
assert doc == u('<EFBFBD>')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'code,call_signature', [
|
||||||
|
('def my_function(x, y, z) -> str:\n return', 'my_function(x, y, z)'),
|
||||||
|
('lambda x, y, z: x + y * z\n', '<lambda>(x, y, z)')
|
||||||
|
])
|
||||||
|
def test_get_call_signature(code, call_signature):
|
||||||
|
node = parse(code).children[0]
|
||||||
|
if node.type == 'simple_stmt':
|
||||||
|
node = node.children[0]
|
||||||
|
assert parser_utils.get_call_signature(node) == call_signature
|
||||||
|
|
||||||
|
assert parser_utils.get_doc_with_call_signature(node) == (call_signature + '\n\n')
|
||||||
@@ -51,3 +51,19 @@ class TestSpeed(TestCase):
|
|||||||
with open('speed/precedence.py') as f:
|
with open('speed/precedence.py') as f:
|
||||||
line = len(f.read().splitlines())
|
line = len(f.read().splitlines())
|
||||||
assert jedi.Script(line=line, path='speed/precedence.py').goto_definitions()
|
assert jedi.Script(line=line, path='speed/precedence.py').goto_definitions()
|
||||||
|
|
||||||
|
@_check_speed(0.1)
|
||||||
|
def test_no_repr_computation(self):
|
||||||
|
"""
|
||||||
|
For Interpreter completion aquisition of sourcefile can trigger
|
||||||
|
unwanted computation of repr(). Exemple : big pandas data.
|
||||||
|
See issue #919.
|
||||||
|
"""
|
||||||
|
class SlowRepr():
|
||||||
|
"class to test what happens if __repr__ is very slow."
|
||||||
|
def some_method(self):
|
||||||
|
pass
|
||||||
|
def __repr__(self):
|
||||||
|
time.sleep(0.2)
|
||||||
|
test = SlowRepr()
|
||||||
|
jedi.Interpreter('test.som', [locals()]).completions()
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import jedi
|
|
||||||
|
|
||||||
def test_path_issues():
|
|
||||||
"""
|
|
||||||
See pull request #684 for details.
|
|
||||||
"""
|
|
||||||
source = '''from datetime import '''
|
|
||||||
assert jedi.Script(source).completions()
|
|
||||||
Reference in New Issue
Block a user