forked from VimPlug/jedi
All modules now have a code_lines attribute, see #1062
This commit is contained in:
@@ -120,6 +120,7 @@ class Script(object):
|
|||||||
)
|
)
|
||||||
debug.speed('parsed')
|
debug.speed('parsed')
|
||||||
self._code_lines = parso.split_lines(source)
|
self._code_lines = parso.split_lines(source)
|
||||||
|
self._code = source
|
||||||
line = max(len(self._code_lines), 1) if line is None else line
|
line = max(len(self._code_lines), 1) if line is None else line
|
||||||
if not (0 < line <= len(self._code_lines)):
|
if not (0 < line <= len(self._code_lines)):
|
||||||
raise ValueError('`line` parameter is not in a valid range.')
|
raise ValueError('`line` parameter is not in a valid range.')
|
||||||
@@ -141,7 +142,10 @@ class Script(object):
|
|||||||
if n is not None:
|
if n is not None:
|
||||||
name = n
|
name = n
|
||||||
|
|
||||||
module = ModuleContext(self._evaluator, self._module_node, self.path)
|
module = ModuleContext(
|
||||||
|
self._evaluator, self._module_node, self.path,
|
||||||
|
code_lines=self._code_lines
|
||||||
|
)
|
||||||
imports.add_module(self._evaluator, name, module)
|
imports.add_module(self._evaluator, name, module)
|
||||||
return module
|
return module
|
||||||
|
|
||||||
@@ -374,7 +378,8 @@ class Interpreter(Script):
|
|||||||
self._evaluator,
|
self._evaluator,
|
||||||
self._module_node,
|
self._module_node,
|
||||||
self.namespaces,
|
self.namespaces,
|
||||||
path=self.path
|
path=self.path,
|
||||||
|
code_lines=self._code_lines,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -377,8 +377,7 @@ class BaseDefinition(object):
|
|||||||
if self.in_builtin_module():
|
if self.in_builtin_module():
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
path = self._name.get_root_context().py__file__()
|
lines = self._name.get_root_context().code_lines
|
||||||
lines = parser_cache[self._evaluator.grammar._hashed][path].lines
|
|
||||||
|
|
||||||
index = self._name.start_pos[0] - 1
|
index = self._name.start_pos[0] - 1
|
||||||
start_index = max(index - before, 0)
|
start_index = max(index - before, 0)
|
||||||
|
|||||||
@@ -21,15 +21,18 @@ class NamespaceObject(object):
|
|||||||
|
|
||||||
|
|
||||||
class MixedModuleContext(Context):
|
class MixedModuleContext(Context):
|
||||||
resets_positions = True
|
|
||||||
type = 'mixed_module'
|
type = 'mixed_module'
|
||||||
|
|
||||||
def __init__(self, evaluator, tree_module, namespaces, path):
|
def __init__(self, evaluator, tree_module, namespaces, path, code_lines):
|
||||||
self.evaluator = evaluator
|
self.evaluator = evaluator
|
||||||
self._namespaces = namespaces
|
self._namespaces = namespaces
|
||||||
|
|
||||||
self._namespace_objects = [NamespaceObject(n) for n in namespaces]
|
self._namespace_objects = [NamespaceObject(n) for n in namespaces]
|
||||||
self._module_context = ModuleContext(evaluator, tree_module, path=path)
|
self._module_context = ModuleContext(
|
||||||
|
evaluator, tree_module,
|
||||||
|
path=path,
|
||||||
|
code_lines=code_lines
|
||||||
|
)
|
||||||
self.tree_node = tree_module
|
self.tree_node = tree_module
|
||||||
|
|
||||||
def get_node(self):
|
def get_node(self):
|
||||||
@@ -50,5 +53,9 @@ class MixedModuleContext(Context):
|
|||||||
for filter in mixed_object.get_filters(*args, **kwargs):
|
for filter in mixed_object.get_filters(*args, **kwargs):
|
||||||
yield filter
|
yield filter
|
||||||
|
|
||||||
|
@property
|
||||||
|
def code_lines(self):
|
||||||
|
return self._module_context.code_lines
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(self._module_context, name)
|
return getattr(self._module_context, name)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ Used only for REPL Completion.
|
|||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from jedi.parser_utils import get_cached_code_lines
|
||||||
|
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.cache import underscore_memoization
|
from jedi.cache import underscore_memoization
|
||||||
@@ -140,10 +142,10 @@ def _find_syntax_node_name(evaluator, access_handle):
|
|||||||
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__)
|
||||||
return None, None, None
|
return None
|
||||||
if path is None or not os.path.exists(path):
|
if path is None or not os.path.exists(path):
|
||||||
# The path might not exist or be e.g. <stdin>.
|
# The path might not exist or be e.g. <stdin>.
|
||||||
return None, None, None
|
return None
|
||||||
|
|
||||||
module_node = _load_module(evaluator, path)
|
module_node = _load_module(evaluator, path)
|
||||||
|
|
||||||
@@ -151,22 +153,23 @@ def _find_syntax_node_name(evaluator, access_handle):
|
|||||||
# We don't need to check names for modules, because there's not really
|
# We don't need to check names for modules, because there's not really
|
||||||
# a way to write a module in a module in Python (and also __name__ can
|
# a way to write a module in a module in Python (and also __name__ can
|
||||||
# be something like ``email.utils``).
|
# be something like ``email.utils``).
|
||||||
return module_node, module_node, path
|
code_lines = get_cached_code_lines(evaluator.grammar, path)
|
||||||
|
return module_node, module_node, path, code_lines
|
||||||
|
|
||||||
try:
|
try:
|
||||||
name_str = python_object.__name__
|
name_str = python_object.__name__
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# Stuff like python_function.__code__.
|
# Stuff like python_function.__code__.
|
||||||
return None, None, None
|
return None
|
||||||
|
|
||||||
if name_str == '<lambda>':
|
if name_str == '<lambda>':
|
||||||
return None, None, None # It's too hard to find lambdas.
|
return None # It's too hard to find lambdas.
|
||||||
|
|
||||||
# Doesn't always work (e.g. os.stat_result)
|
# Doesn't always work (e.g. os.stat_result)
|
||||||
try:
|
try:
|
||||||
names = module_node.get_used_names()[name_str]
|
names = module_node.get_used_names()[name_str]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None, None, None
|
return None
|
||||||
names = [n for n in names if n.is_definition()]
|
names = [n for n in names if n.is_definition()]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -183,29 +186,36 @@ def _find_syntax_node_name(evaluator, access_handle):
|
|||||||
# There's a chance that the object is not available anymore, because
|
# There's a chance that the object is not available anymore, because
|
||||||
# the code has changed in the background.
|
# the code has changed in the background.
|
||||||
if line_names:
|
if line_names:
|
||||||
return module_node, line_names[-1].parent, path
|
names = line_names
|
||||||
|
|
||||||
|
code_lines = get_cached_code_lines(evaluator.grammar, path)
|
||||||
# It's really hard to actually get the right definition, here as a last
|
# It's really hard to actually get the right definition, here as a last
|
||||||
# resort we just return the last one. This chance might lead to odd
|
# resort we just return the last one. This chance might lead to odd
|
||||||
# completions at some points but will lead to mostly correct type
|
# completions at some points but will lead to mostly correct type
|
||||||
# inference, because people tend to define a public name in a module only
|
# inference, because people tend to define a public name in a module only
|
||||||
# once.
|
# once.
|
||||||
return module_node, names[-1].parent, path
|
return module_node, names[-1].parent, path, code_lines
|
||||||
|
|
||||||
|
|
||||||
@compiled_objects_cache('mixed_cache')
|
@compiled_objects_cache('mixed_cache')
|
||||||
def _create(evaluator, access_handle, parent_context, *args):
|
def _create(evaluator, access_handle, parent_context, *args):
|
||||||
module_node, tree_node, path = _find_syntax_node_name(evaluator, access_handle)
|
|
||||||
|
|
||||||
compiled_object = create_cached_compiled_object(
|
compiled_object = create_cached_compiled_object(
|
||||||
evaluator, access_handle, parent_context=parent_context.compiled_object)
|
evaluator, access_handle, parent_context=parent_context.compiled_object)
|
||||||
if tree_node is None:
|
|
||||||
|
result = _find_syntax_node_name(evaluator, access_handle)
|
||||||
|
if result is None:
|
||||||
return compiled_object
|
return compiled_object
|
||||||
|
|
||||||
|
module_node, tree_node, path, code_lines = result
|
||||||
|
|
||||||
if parent_context.tree_node.get_root_node() == module_node:
|
if parent_context.tree_node.get_root_node() == module_node:
|
||||||
module_context = parent_context.get_root_context()
|
module_context = parent_context.get_root_context()
|
||||||
else:
|
else:
|
||||||
module_context = ModuleContext(evaluator, module_node, path=path)
|
module_context = ModuleContext(
|
||||||
|
evaluator, module_node,
|
||||||
|
path=path,
|
||||||
|
code_lines=code_lines,
|
||||||
|
)
|
||||||
# TODO this __name__ is probably wrong.
|
# TODO this __name__ is probably wrong.
|
||||||
name = compiled_object.get_root_context().py__name__()
|
name = compiled_object.get_root_context().py__name__()
|
||||||
imports.add_module(evaluator, name, module_context)
|
imports.add_module(evaluator, name, module_context)
|
||||||
|
|||||||
@@ -43,10 +43,11 @@ class ModuleContext(TreeContext):
|
|||||||
api_type = u'module'
|
api_type = u'module'
|
||||||
parent_context = None
|
parent_context = None
|
||||||
|
|
||||||
def __init__(self, evaluator, module_node, path):
|
def __init__(self, evaluator, module_node, path, code_lines):
|
||||||
super(ModuleContext, self).__init__(evaluator, parent_context=None)
|
super(ModuleContext, self).__init__(evaluator, parent_context=None)
|
||||||
self.tree_node = module_node
|
self.tree_node = module_node
|
||||||
self._path = path
|
self._path = path
|
||||||
|
self.code_lines = code_lines
|
||||||
|
|
||||||
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
||||||
yield MergedFilter(
|
yield MergedFilter(
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from parso import python_bytes_to_unicode
|
|||||||
from jedi._compatibility import unicode, ImplicitNSInfo, force_unicode
|
from jedi._compatibility import unicode, ImplicitNSInfo, force_unicode
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
|
from jedi.parser_utils import get_cached_code_lines
|
||||||
from jedi.evaluate import sys_path
|
from jedi.evaluate import sys_path
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
@@ -492,7 +493,11 @@ def _load_module(evaluator, path=None, code=None, sys_path=None,
|
|||||||
cache_path=settings.cache_directory)
|
cache_path=settings.cache_directory)
|
||||||
|
|
||||||
from jedi.evaluate.context import ModuleContext
|
from jedi.evaluate.context import ModuleContext
|
||||||
module = ModuleContext(evaluator, module_node, path=path)
|
module = ModuleContext(
|
||||||
|
evaluator, module_node,
|
||||||
|
path=path,
|
||||||
|
code_lines=get_cached_code_lines(evaluator.grammar, path),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
module = compiled.load_module(evaluator, path=path, sys_path=sys_path)
|
module = compiled.load_module(evaluator, path=path, sys_path=sys_path)
|
||||||
add_module(evaluator, module_name, module, safe=safe_module_name)
|
add_module(evaluator, module_name, module, safe=safe_module_name)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ x support for type hint comments for functions, `# type: (int, str) -> int`.
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from parso import ParserSyntaxError, parse
|
from parso import ParserSyntaxError, parse, split_lines
|
||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
|
|
||||||
from jedi._compatibility import unicode, force_unicode
|
from jedi._compatibility import unicode, force_unicode
|
||||||
@@ -216,6 +216,7 @@ def infer_return_types(function_context):
|
|||||||
|
|
||||||
|
|
||||||
_typing_module = None
|
_typing_module = None
|
||||||
|
_typing_module_code_lines = None
|
||||||
|
|
||||||
|
|
||||||
def _get_typing_replacement_module(grammar):
|
def _get_typing_replacement_module(grammar):
|
||||||
@@ -223,14 +224,15 @@ def _get_typing_replacement_module(grammar):
|
|||||||
The idea is to return our jedi replacement for the PEP-0484 typing module
|
The idea is to return our jedi replacement for the PEP-0484 typing module
|
||||||
as discussed at https://github.com/davidhalter/jedi/issues/663
|
as discussed at https://github.com/davidhalter/jedi/issues/663
|
||||||
"""
|
"""
|
||||||
global _typing_module
|
global _typing_module, _typing_module_code_lines
|
||||||
if _typing_module is None:
|
if _typing_module is None:
|
||||||
typing_path = \
|
typing_path = \
|
||||||
os.path.abspath(os.path.join(__file__, "../jedi_typing.py"))
|
os.path.abspath(os.path.join(__file__, "../jedi_typing.py"))
|
||||||
with open(typing_path) as f:
|
with open(typing_path) as f:
|
||||||
code = unicode(f.read())
|
code = unicode(f.read())
|
||||||
_typing_module = grammar.parse(code)
|
_typing_module = grammar.parse(code)
|
||||||
return _typing_module
|
_typing_module_code_lines = split_lines(code)
|
||||||
|
return _typing_module, _typing_module_code_lines
|
||||||
|
|
||||||
|
|
||||||
def py__getitem__(context, typ, node):
|
def py__getitem__(context, typ, node):
|
||||||
@@ -260,10 +262,12 @@ def py__getitem__(context, typ, node):
|
|||||||
# check for the instance typing._Optional (Python 3.6).
|
# check for the instance typing._Optional (Python 3.6).
|
||||||
return context.eval_node(nodes[0])
|
return context.eval_node(nodes[0])
|
||||||
|
|
||||||
|
module_node, code_lines = _get_typing_replacement_module(context.evaluator.latest_grammar)
|
||||||
typing = ModuleContext(
|
typing = ModuleContext(
|
||||||
context.evaluator,
|
context.evaluator,
|
||||||
module_node=_get_typing_replacement_module(context.evaluator.latest_grammar),
|
module_node=module_node,
|
||||||
path=None
|
path=None,
|
||||||
|
code_lines=code_lines,
|
||||||
)
|
)
|
||||||
factories = typing.py__getattribute__("factory")
|
factories = typing.py__getattribute__("factory")
|
||||||
assert len(factories) == 1
|
assert len(factories) == 1
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ compiled module that returns the types for C-builtins.
|
|||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import parso
|
||||||
|
|
||||||
from jedi._compatibility import force_unicode
|
from jedi._compatibility import force_unicode
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.evaluate.arguments import ValuesArguments
|
from jedi.evaluate.arguments import ValuesArguments
|
||||||
@@ -293,8 +295,8 @@ def collections_namedtuple(evaluator, obj, arguments):
|
|||||||
|
|
||||||
base = next(iter(_class_template_set)).get_safe_value()
|
base = next(iter(_class_template_set)).get_safe_value()
|
||||||
base += _NAMEDTUPLE_INIT
|
base += _NAMEDTUPLE_INIT
|
||||||
# Build source
|
# Build source code
|
||||||
source = base.format(
|
code = base.format(
|
||||||
typename=name,
|
typename=name,
|
||||||
field_names=tuple(fields),
|
field_names=tuple(fields),
|
||||||
num_fields=len(fields),
|
num_fields=len(fields),
|
||||||
@@ -304,10 +306,13 @@ def collections_namedtuple(evaluator, obj, arguments):
|
|||||||
for index, name in enumerate(fields))
|
for index, name in enumerate(fields))
|
||||||
)
|
)
|
||||||
|
|
||||||
# Parse source
|
# Parse source code
|
||||||
module = evaluator.grammar.parse(source)
|
module = evaluator.grammar.parse(code)
|
||||||
generated_class = next(module.iter_classdefs())
|
generated_class = next(module.iter_classdefs())
|
||||||
parent_context = ModuleContext(evaluator, module, '')
|
parent_context = ModuleContext(
|
||||||
|
evaluator, module, None,
|
||||||
|
code_lines=parso.split_lines(code),
|
||||||
|
)
|
||||||
return ContextSet(ClassContext(evaluator, parent_context, generated_class))
|
return ContextSet(ClassContext(evaluator, parent_context, generated_class))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from jedi.evaluate.cache import evaluator_method_cache
|
|||||||
from jedi.evaluate.base_context import ContextualizedNode
|
from jedi.evaluate.base_context import ContextualizedNode
|
||||||
from jedi.evaluate.helpers import is_string
|
from jedi.evaluate.helpers import is_string
|
||||||
from jedi.common.utils import traverse_parents
|
from jedi.common.utils import traverse_parents
|
||||||
|
from jedi.parser_utils import get_cached_code_lines
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
|
|
||||||
@@ -150,7 +151,10 @@ def _get_paths_from_buildout_script(evaluator, buildout_script_path):
|
|||||||
return
|
return
|
||||||
|
|
||||||
from jedi.evaluate.context import ModuleContext
|
from jedi.evaluate.context import ModuleContext
|
||||||
module = ModuleContext(evaluator, module_node, buildout_script_path)
|
module = ModuleContext(
|
||||||
|
evaluator, module_node, buildout_script_path,
|
||||||
|
code_lines=get_cached_code_lines(evaluator.grammar, buildout_script_path),
|
||||||
|
)
|
||||||
for path in check_sys_path_modifications(module):
|
for path in check_sys_path_modifications(module):
|
||||||
yield path
|
yield path
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import textwrap
|
import textwrap
|
||||||
from inspect import cleandoc
|
from inspect import cleandoc
|
||||||
|
|
||||||
from jedi._compatibility import literal_eval, force_unicode
|
|
||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
|
from parso.cache import parser_cache
|
||||||
|
|
||||||
|
from jedi._compatibility import literal_eval, force_unicode
|
||||||
|
|
||||||
_EXECUTE_NODES = {'funcdef', 'classdef', 'import_from', 'import_name', 'test',
|
_EXECUTE_NODES = {'funcdef', 'classdef', 'import_from', 'import_name', 'test',
|
||||||
'or_test', 'and_test', 'not_test', 'comparison', 'expr',
|
'or_test', 'and_test', 'not_test', 'comparison', 'expr',
|
||||||
@@ -238,3 +240,11 @@ def get_parent_scope(node, include_flows=False):
|
|||||||
break
|
break
|
||||||
scope = scope.parent
|
scope = scope.parent
|
||||||
return scope
|
return scope
|
||||||
|
|
||||||
|
|
||||||
|
def get_cached_code_lines(grammar, path):
|
||||||
|
"""
|
||||||
|
Basically access the cached code lines in parso. This is not the nicest way
|
||||||
|
to do this, but we avoid splitting all the lines again.
|
||||||
|
"""
|
||||||
|
return parser_cache[grammar._hashed][path].lines
|
||||||
|
|||||||
@@ -71,3 +71,22 @@ def test_nested_namedtuples(Script):
|
|||||||
train_x.train.'''
|
train_x.train.'''
|
||||||
))
|
))
|
||||||
assert 'data' in [c.name for c in s.completions()]
|
assert 'data' in [c.name for c in s.completions()]
|
||||||
|
|
||||||
|
|
||||||
|
def test_namedtuple_goto_definitions(Script):
|
||||||
|
source = dedent("""
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
Foo = namedtuple('Foo', 'id timestamp gps_timestamp attributes')
|
||||||
|
Foo""")
|
||||||
|
|
||||||
|
from jedi.api import Script
|
||||||
|
|
||||||
|
lines = source.split("\n")
|
||||||
|
d1, = Script(source).goto_definitions()
|
||||||
|
|
||||||
|
print(d1)
|
||||||
|
print(d1.line)
|
||||||
|
print(d1.module_path)
|
||||||
|
print(d1.get_line_code())
|
||||||
|
assert d1.get_line_code() == lines[-1]
|
||||||
|
|||||||
Reference in New Issue
Block a user