1
0
forked from VimPlug/jedi

Merge branch 'dev'

This commit is contained in:
Dave Halter
2017-08-12 22:46:24 +02:00
43 changed files with 580 additions and 542 deletions

View File

@@ -166,14 +166,6 @@ try:
except NameError:
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
if is_py3:

View File

@@ -342,7 +342,7 @@ class Script(object):
elif isinstance(node, tree.Import):
import_names = set(node.get_defined_names())
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:
imports.infer_import(context, n)
elif node.type == 'expr_stmt':

View File

@@ -15,7 +15,7 @@ from jedi.evaluate import representation as er
from jedi.evaluate import instance
from jedi.evaluate import imports
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.api.keywords import KeywordName
@@ -538,7 +538,12 @@ class Definition(BaseDefinition):
typ = 'def'
return typ + ' ' + u(self._name.string_name)
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()
# Remove the prefix, because that's not what we want for get_code

View File

@@ -76,12 +76,16 @@ class KeywordName(AbstractNameDefinition):
api_type = 'keyword'
def __init__(self, evaluator, name):
self.evaluator = evaluator
self.string_name = name
self.parent_context = evaluator.BUILTINS
def eval(self):
return set()
def infer(self):
return [Keyword(self.evaluator, self.string_name, (0, 0))]
class Keyword(object):
api_type = 'keyword'
@@ -100,9 +104,8 @@ class Keyword(object):
""" For a `parsing.Name` like comparision """
return [self.name]
@property
def docstr(self):
return imitate_pydoc(self.name)
def py__doc__(self, include_call_signature=False):
return imitate_pydoc(self.name.string_name)
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self.name)
@@ -136,6 +139,6 @@ def imitate_pydoc(string):
return ''
try:
return pydoc_topics.topics[label] if pydoc_topics else ''
return pydoc_topics.topics[label].strip() if pydoc_topics else ''
except KeyError:
return ''

View File

@@ -194,7 +194,7 @@ class Evaluator(object):
if_stmt = if_stmt.parent
if if_stmt.type in ('if_stmt', 'for_stmt'):
break
if if_stmt.is_scope():
if parser_utils.is_scope(if_stmt):
if_stmt = None
break
predefined_if_name_dict = context.predefined_names.get(if_stmt)
@@ -337,7 +337,7 @@ class Evaluator(object):
# This is the first global lookup.
stmt = atom.get_definition()
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':
# We only need to adjust the start_pos for statements, because
# there the name cannot be used.
@@ -537,7 +537,7 @@ class Evaluator(object):
while True:
node = node.parent
if node.is_scope():
if parser_utils.is_scope(node):
return node
elif node.type in ('argument', 'testlist_comp'):
if node.children[1].type == 'comp_for':
@@ -553,7 +553,7 @@ class Evaluator(object):
return base_context
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)
if is_funcdef:
@@ -586,7 +586,7 @@ class Evaluator(object):
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
else:
if node.parent.type in ('funcdef', 'classdef'):

View File

@@ -222,9 +222,6 @@ class CompiledObject(Context):
for result in self.evaluator.execute(bltn_obj, params):
yield result
def is_scope(self):
return True
def get_self_attributes(self):
return [] # Instance compatibility

View File

@@ -116,8 +116,21 @@ def _load_module(evaluator, path, python_object):
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):
try:
if not source_findable(python_object):
raise TypeError # Prevents computation of `repr` within inspect.
path = inspect.getsourcefile(python_object)
except TypeError:
# The type might not be known (e.g. class_with_dict.__weakref__)

View File

@@ -24,6 +24,7 @@ from jedi.evaluate.cache import memoize_default
from jedi.evaluate import imports
from jedi.evaluate.param import TreeArguments, create_default_param
from jedi.common import to_list, unite
from jedi.parser_utils import get_parent_scope
MAX_PARAM_SEARCHES = 20
@@ -103,7 +104,7 @@ def _search_function_executions(evaluator, module_context, funcdef):
func_string_name = funcdef.name.value
compare_node = funcdef
if func_string_name == '__init__':
cls = funcdef.get_parent_scope()
cls = get_parent_scope(funcdef)
if isinstance(cls, tree.Class):
func_string_name = cls.name.value
compare_node = cls

View File

@@ -7,6 +7,7 @@ from abc import abstractmethod
from jedi.parser.tree import search_ancestor
from jedi.evaluate import flow_analysis
from jedi.common import to_list, unite
from jedi.parser_utils import get_parent_scope
class AbstractNameDefinition(object):
@@ -189,7 +190,7 @@ class ParserTreeFilter(AbstractUsedNamesFilter):
if parent.type == 'trailer':
return False
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):
for name in sorted(names, key=lambda name: name.start_pos, reverse=True):

View File

@@ -32,6 +32,7 @@ from jedi.evaluate import param
from jedi.evaluate import helpers
from jedi.evaluate.filters import get_global_filters
from jedi.evaluate.context import ContextualizedName, ContextualizedNode
from jedi.parser_utils import is_scope, get_parent_scope
class NameFinder(object):
@@ -106,7 +107,7 @@ class NameFinder(object):
if self._context.predefined_names:
# TODO is this ok? node might not always be a tree.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
if node.type in ("if_stmt", "for_stmt", "comp_for"):
try:
@@ -160,7 +161,7 @@ class NameFinder(object):
if base_node.type == 'comp_for':
return types
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,
self._name, self._position)
if n is not None:
@@ -298,7 +299,7 @@ def _check_flow_information(context, flow, search_name, pos):
return None
result = None
if flow.is_scope():
if is_scope(flow):
# Check for asserts.
module_node = flow.get_root_node()
try:

View File

@@ -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):
lookup_table = {}
@@ -32,14 +34,14 @@ UNSURE = Status(None, 'unsure')
def _get_flow_scopes(node):
while True:
node = node.get_parent_scope(include_flows=True)
if node is None or node.is_scope():
node = get_parent_scope(node, include_flows=True)
if node is None or is_scope(node):
return
yield node
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:
origin_flow_scopes = list(_get_flow_scopes(origin_scope))
node_flow_scopes = list(_get_flow_scopes(node))
@@ -95,7 +97,7 @@ def _break_check(context, context_scope, flow_scope, node):
return reachable
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)
else:
return reachable

View File

@@ -3,6 +3,7 @@ from itertools import chain
from contextlib import contextmanager
from jedi.parser.python import tree
from jedi.parser_utils import get_parent_scope
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
# node have the parent module and not suite as all the others.
# 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

View File

@@ -37,7 +37,7 @@ from jedi.evaluate.filters import AbstractNameDefinition
@memoize_default(default=set())
def infer_import(context, tree_name, is_goto=False):
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)
from_import_name = None
evaluator = context.evaluator

View File

@@ -11,6 +11,7 @@ from jedi.cache import memoize_method
from jedi.evaluate import representation as er
from jedi.evaluate.dynamic import search_params
from jedi.evaluate import iterable
from jedi.parser_utils import get_parent_scope
class AbstractInstanceContext(Context):
@@ -151,7 +152,7 @@ class AbstractInstanceContext(Context):
def create_instance_context(self, class_context, node):
if node.parent.type in ('funcdef', 'classdef'):
node = node.parent
scope = node.get_parent_scope()
scope = get_parent_scope(node)
if scope == class_context.tree_node:
return class_context
else:
@@ -189,13 +190,18 @@ class CompiledInstance(AbstractInstanceContext):
return compiled.CompiledContextName(self, self.class_context.name.string_name)
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
else:
return super(CompiledInstance, self).create_instance_context(class_context, node)
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
def name(self):
return filters.ContextName(self, self.class_context.name.tree_name)
@@ -332,7 +338,7 @@ class InstanceClassFilter(filters.ParserTreeFilter):
while node is not None:
if node == self._parser_scope or node == self.context:
return True
node = node.get_parent_scope()
node = get_parent_scope(node)
return False
def _access_possible(self, name):

View File

@@ -276,7 +276,7 @@ class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext)):
def py__class__(self):
# This differentiation is only necessary for Python2. Python3 does not
# 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'
else:
name = 'FUNCTION_CLASS'
@@ -350,8 +350,8 @@ class FunctionExecutionContext(context.TreeContext):
@recursion.execution_recursion_decorator(default=iter([]))
def get_yield_values(self):
for_parents = [(y, tree.search_ancestor(y, ('for_stmt', 'funcdef',
'while_stmt', 'if_stmt')))
for_parents = [(y, tree.search_ancestor(y, 'for_stmt', 'funcdef',
'while_stmt', 'if_stmt'))
for y in self.tree_node.iter_yield_exprs()]
# Calculate if the yields are placed within the same for loop.
@@ -476,7 +476,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext)):
modules = []
for i in self.tree_node.iter_imports():
if i.is_star_import():
name = i.star_import_name()
name = i.get_paths()[-1][-1]
new = imports.infer_import(self, name)
for module in new:
if isinstance(module, ModuleContext):

View File

@@ -74,9 +74,6 @@ def parse(code=None, path=None, grammar=None, error_recovery=True,
if grammar is None:
grammar = load_grammar()
if path is not None:
path = os.path.expanduser(path)
if cache and not code and path is not None:
# In this case we do actual caching. We just try to load it.
module_node = load_module(grammar, path)

View File

@@ -144,22 +144,9 @@ class Parser(BaseParser):
elif symbol == 'suite' and len(nodes) > 1:
# suites without an indent in them get discarded.
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
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)
if self._stack_removal(grammar, stack, arcs, index + 1, value, start_pos):

View File

@@ -25,8 +25,6 @@ Any subclasses of :class:`Scope`, including :class:`Module` has an attribute
[<ImportName: import os@1,0>]
"""
from itertools import chain
from jedi._compatibility import utf8_repr, unicode
from jedi.parser.tree import Node, BaseNode, Leaf, ErrorNode, ErrorLeaf, \
search_ancestor
@@ -61,18 +59,10 @@ class DocstringMixin(object):
class PythonMixin(object):
def get_parent_scope(self, include_flows=False):
"""
Returns the underlying scope.
"""
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
"""
Some Python specific utitilies.
"""
__slots__ = ()
def get_definition(self):
if self.type in ('newline', 'endmarker'):
@@ -92,10 +82,6 @@ class PythonMixin(object):
break
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):
for c in self.children:
if isinstance(c, Leaf):
@@ -269,9 +255,6 @@ class Scope(PythonBaseNode, DocstringMixin):
return scan(self.children)
def is_scope(self):
return True
def get_suite(self):
"""
Returns the part that is executed by the function.
@@ -311,7 +294,7 @@ class Module(Scope):
# the future print statement).
for imp in self.iter_imports():
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]
if len(names) == 2 and names[0] == '__future__':
yield names[1]
@@ -328,6 +311,10 @@ class Module(Scope):
return False
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:
# Don't directly use self._used_names to eliminate a lookup.
dct = {}
@@ -358,9 +345,15 @@ class ClassOrFunc(Scope):
@property
def name(self):
"""
Returns the `Name` leaf that defines the function or class name.
"""
return self.children[1]
def get_decorators(self):
"""
:return list of Decorator:
"""
decorated = self.parent
if decorated.type == 'decorated':
if decorated.children[0].type == 'decorators':
@@ -389,6 +382,10 @@ class Class(ClassOrFunc):
super(Class, self).__init__(children)
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
return None
else:
@@ -452,14 +449,15 @@ class Function(ClassOrFunc):
"""
Used to store the parsed contents of a python function.
Children:
0. <Keyword: def>
1. <Name>
2. parameter list (including open-paren and close-paren <Operator>s)
3. or 5. <Operator: :>
4. or 6. Node() representing function body
3. -> (if annotation is also present)
4. annotation (if present)
Children::
0. <Keyword: def>
1. <Name>
2. parameter list (including open-paren and close-paren <Operator>s)
3. or 5. <Operator: :>
4. or 6. Node() representing function body
3. -> (if annotation is also present)
4. annotation (if present)
"""
type = 'funcdef'
@@ -514,20 +512,16 @@ class Function(ClassOrFunc):
except IndexError:
return None
def _get_paramlist_code(self):
return self.children[2].get_code()
class Lambda(Function):
"""
Lambdas are basically trimmed functions, so give it the same interface.
Children:
Children::
0. <Keyword: lambda>
*. <Param x> for each argument x
-2. <Operator: :>
-1. Node() representing body
0. <Keyword: lambda>
*. <Param x> for each argument x
-2. <Operator: :>
-1. Node() representing body
"""
type = 'lambdef'
__slots__ = ()
@@ -545,9 +539,6 @@ class Lambda(Function):
"""
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):
return self.children[1:-2]
@@ -556,7 +547,6 @@ class Lambda(Function):
"""
Returns `None`, lambdas don't have annotations.
"""
# lambda functions do not support annotations
return None
def __repr__(self):
@@ -650,6 +640,10 @@ class WithStmt(Flow):
__slots__ = ()
def get_defined_names(self):
"""
Returns the a list of `Name` that the with statement defines. The
defined names are set after `as`.
"""
names = []
for with_item in self.children[1:-2:2]:
# Check with items for 'as' names.
@@ -669,13 +663,18 @@ class Import(PythonBaseNode):
__slots__ = ()
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:
# 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:
pass
for path in self.paths():
for path in self.get_paths():
if name in path:
return path[:path.index(name) + 1]
raise ValueError('Name should be defined in the import itself')
@@ -692,9 +691,14 @@ class ImportFrom(Import):
__slots__ = ()
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()]
def aliases(self):
def _aliases(self):
"""Mapping from alias to its corresponding name."""
return dict((alias, name) for name, alias in self._as_name_tuples()
if alias is not None)
@@ -738,16 +742,12 @@ class ImportFrom(Import):
else:
yield as_name.children[::2] # yields x, y -> ``x as y``
def star_import_name(self):
"""
The last name defined in a star import.
"""
return self.paths()[-1][-1]
def paths(self):
def get_paths(self):
"""
The import paths defined in an import statement. Typically an array
like this: ``[<Name: datetime>, <Name: date>]``.
:return list of list of Name:
"""
dotted = self.get_from_names()
@@ -762,6 +762,11 @@ class ImportName(Import):
__slots__ = ()
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()]
@property
@@ -769,7 +774,7 @@ class ImportName(Import):
"""The level parameter of ``__import__``."""
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()]
def _dotted_as_names(self):
@@ -799,10 +804,13 @@ class ImportName(Import):
import foo.bar
"""
return [1 for path, alias in self._dotted_as_names()
if alias is None and len(path) > 1]
return bool([1 for path, alias in self._dotted_as_names()
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()
if alias is not None)
@@ -880,14 +888,18 @@ class ExprStmt(PythonBaseNode, DocstringMixin):
__slots__ = ()
def get_defined_names(self):
"""
Returns a list of `Name` defined before the `=` sign.
"""
names = []
if self.children[1].type == 'annassign':
names = _defined_names(self.children[0])
return list(chain.from_iterable(
_defined_names(self.children[i])
return [
name
for i in range(0, len(self.children) - 2, 2)
if '=' in self.children[i + 1].value)
) + names
if '=' in self.children[i + 1].value
for name in _defined_names(self.children[i])
] + names
def get_rhs(self):
"""Returns the right-hand-side of the equals."""
@@ -925,6 +937,10 @@ class Param(PythonBaseNode):
@property
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]
if first in ('*', '**'):
return len(first.value)
@@ -932,6 +948,10 @@ class Param(PythonBaseNode):
@property
def default(self):
"""
The default is the test node that appears after the `=`. Is `None` in
case no default is present.
"""
try:
return self.children[int(self.children[0] in ('*', '**')) + 2]
except IndexError:
@@ -939,6 +959,10 @@ class Param(PythonBaseNode):
@property
def annotation(self):
"""
The default is the test node that appears after `->`. Is `None` in case
no annotation is present.
"""
tfpdef = self._tfpdef()
if tfpdef.type == 'tfpdef':
assert tfpdef.children[1] == ":"
@@ -957,6 +981,9 @@ class Param(PythonBaseNode):
@property
def name(self):
"""
The `Name` leaf of the param.
"""
if self._tfpdef().type == 'tfpdef':
return self._tfpdef().children[0]
else:
@@ -965,7 +992,7 @@ class Param(PythonBaseNode):
@property
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)
try:
@@ -979,16 +1006,28 @@ class Param(PythonBaseNode):
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
if 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):
default = '' if self.default is None else '=%s' % self.default.get_code()
@@ -999,8 +1038,8 @@ class CompFor(PythonBaseNode):
type = 'comp_for'
__slots__ = ()
def is_scope(self):
return True
def get_defined_names(self):
"""
Returns the a list of `Name` that the comprehension defines.
"""
return _defined_names(self.children[1])

View File

@@ -2,21 +2,18 @@ from abc import abstractmethod, abstractproperty
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
match.
: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.
"""
if not isinstance(node_type_or_types, (list, tuple)):
node_type_or_types = (node_type_or_types,)
while True:
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

View File

@@ -154,7 +154,12 @@ def get_call_signature(funcdef, width=72, call_string=None):
call_string = '<lambda>'
else:
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))
@@ -217,3 +222,21 @@ def get_following_comment_same_line(node):
comment = comment[:comment.index("\n")]
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

View File

@@ -115,7 +115,7 @@ else:
'jedi')
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
``~/Library/Caches/Jedi/`` and on Windows to ``%APPDATA%\\Jedi\\Jedi\\``.