forked from VimPlug/jedi
Merge pull request #130 from tkf/list_definitions
Add new API: jedi.api.get_definitions
This commit is contained in:
19
jedi/api.py
19
jedi/api.py
@@ -468,6 +468,25 @@ class Script(object):
|
|||||||
api_classes._clear_caches()
|
api_classes._clear_caches()
|
||||||
|
|
||||||
|
|
||||||
|
def defined_names(source, source_path=None, source_encoding='utf-8'):
|
||||||
|
"""
|
||||||
|
Get all definitions in `source` sorted by its position.
|
||||||
|
|
||||||
|
This functions can be used for listing functions, classes and
|
||||||
|
data defined in a file. This can be useful if you want to list
|
||||||
|
them in "sidebar". Each element in the returned list also has
|
||||||
|
`defined_names` method which can be used to get sub-definitions
|
||||||
|
(e.g., methods in class).
|
||||||
|
|
||||||
|
:rtype: list of api_classes.Definition
|
||||||
|
"""
|
||||||
|
parser = parsing.Parser(
|
||||||
|
modules.source_to_unicode(source, source_encoding),
|
||||||
|
module_path=source_path,
|
||||||
|
)
|
||||||
|
return api_classes.defined_names(parser.scope)
|
||||||
|
|
||||||
|
|
||||||
def set_debug_function(func_cb=debug.print_to_stdout, warnings=True,
|
def set_debug_function(func_cb=debug.print_to_stdout, warnings=True,
|
||||||
notices=True, speed=True):
|
notices=True, speed=True):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import re
|
|||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from _compatibility import unicode
|
from _compatibility import unicode, next
|
||||||
import cache
|
import cache
|
||||||
import dynamic
|
import dynamic
|
||||||
import recursion
|
import recursion
|
||||||
@@ -58,6 +58,9 @@ class BaseDefinition(object):
|
|||||||
def __init__(self, definition, start_pos):
|
def __init__(self, definition, start_pos):
|
||||||
self.start_pos = start_pos
|
self.start_pos = start_pos
|
||||||
self.definition = definition
|
self.definition = definition
|
||||||
|
"""
|
||||||
|
An instance of :class:`jedi.parsing_representation.Base` subclass.
|
||||||
|
"""
|
||||||
self.is_keyword = isinstance(definition, keywords.Keyword)
|
self.is_keyword = isinstance(definition, keywords.Keyword)
|
||||||
|
|
||||||
# generate a path to the definition
|
# generate a path to the definition
|
||||||
@@ -271,6 +274,40 @@ class Definition(BaseDefinition):
|
|||||||
def __init__(self, definition):
|
def __init__(self, definition):
|
||||||
super(Definition, self).__init__(definition, definition.start_pos)
|
super(Definition, self).__init__(definition, definition.start_pos)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Name of variable/function/class/module.
|
||||||
|
|
||||||
|
For example, for ``x = None`` it returns ``'x'``.
|
||||||
|
|
||||||
|
:rtype: str or None
|
||||||
|
"""
|
||||||
|
d = self.definition
|
||||||
|
if isinstance(d, er.InstanceElement):
|
||||||
|
d = d.var
|
||||||
|
|
||||||
|
if isinstance(d, pr.Name):
|
||||||
|
return d.names[-1] if d.names else None
|
||||||
|
elif isinstance(d, er.Array):
|
||||||
|
return unicode(d.type)
|
||||||
|
elif isinstance(d, (pr.Class, er.Class, er.Instance,
|
||||||
|
er.Function, pr.Function)):
|
||||||
|
return unicode(d.name)
|
||||||
|
elif isinstance(d, pr.Module):
|
||||||
|
return self.module_name
|
||||||
|
elif isinstance(d, pr.Import):
|
||||||
|
try:
|
||||||
|
return d.get_defined_names()[0].names[-1]
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
return None
|
||||||
|
elif isinstance(d, pr.Statement):
|
||||||
|
try:
|
||||||
|
return d.assignment_details[0][1].values[0][0].name.names[-1]
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
"""
|
"""
|
||||||
@@ -317,6 +354,32 @@ class Definition(BaseDefinition):
|
|||||||
position = ''
|
position = ''
|
||||||
return "%s:%s%s" % (self.module_name, self.description, position)
|
return "%s:%s%s" % (self.module_name, self.description, position)
|
||||||
|
|
||||||
|
def defined_names(self):
|
||||||
|
"""
|
||||||
|
List sub-definitions (e.g., methods in class).
|
||||||
|
|
||||||
|
:rtype: list of Definition
|
||||||
|
"""
|
||||||
|
d = self.definition
|
||||||
|
if isinstance(d, er.InstanceElement):
|
||||||
|
d = d.var
|
||||||
|
if isinstance(d, pr.Name):
|
||||||
|
d = d.parent
|
||||||
|
return defined_names(d)
|
||||||
|
|
||||||
|
|
||||||
|
def defined_names(scope):
|
||||||
|
"""
|
||||||
|
List sub-definitions (e.g., methods in class).
|
||||||
|
|
||||||
|
:type scope: Scope
|
||||||
|
:rtype: list of Definition
|
||||||
|
"""
|
||||||
|
pair = next(evaluate.get_names_of_scope(
|
||||||
|
scope, star_search=False, include_builtin=False), None)
|
||||||
|
names = pair[1] if pair else []
|
||||||
|
return [Definition(d) for d in sorted(names, key=lambda s: s.start_pos)]
|
||||||
|
|
||||||
|
|
||||||
class RelatedName(BaseDefinition):
|
class RelatedName(BaseDefinition):
|
||||||
"""TODO: document this"""
|
"""TODO: document this"""
|
||||||
|
|||||||
@@ -153,6 +153,8 @@ def get_names_of_scope(scope, position=None, star_search=True,
|
|||||||
>>> pairs[2] #doctest: +ELLIPSIS
|
>>> pairs[2] #doctest: +ELLIPSIS
|
||||||
(<Module: ...builtin...>, [<Name: ...>, ...])
|
(<Module: ...builtin...>, [<Name: ...>, ...])
|
||||||
|
|
||||||
|
:rtype: [(pr.Scope, [pr.Name])]
|
||||||
|
:return: Return an generator that yields a pair of scope and names.
|
||||||
"""
|
"""
|
||||||
in_func_scope = scope
|
in_func_scope = scope
|
||||||
non_flow = scope.get_parent_until(pr.Flow, reverse=True)
|
non_flow = scope.get_parent_until(pr.Flow, reverse=True)
|
||||||
|
|||||||
@@ -391,6 +391,60 @@ class TestFeature(TestBase):
|
|||||||
self.assertEqual(quick_values, real_values)
|
self.assertEqual(quick_values, real_values)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetDefinitions(TestBase):
|
||||||
|
|
||||||
|
def test_get_definitions_flat(self):
|
||||||
|
definitions = api.defined_names("""
|
||||||
|
import module
|
||||||
|
class Class:
|
||||||
|
pass
|
||||||
|
def func():
|
||||||
|
pass
|
||||||
|
data = None
|
||||||
|
""")
|
||||||
|
self.assertEqual([d.name for d in definitions],
|
||||||
|
['module', 'Class', 'func', 'data'])
|
||||||
|
|
||||||
|
def test_dotted_assignment(self):
|
||||||
|
definitions = api.defined_names("""
|
||||||
|
x = Class()
|
||||||
|
x.y.z = None
|
||||||
|
""")
|
||||||
|
self.assertEqual([d.name for d in definitions],
|
||||||
|
['x'])
|
||||||
|
|
||||||
|
def test_multiple_assignment(self):
|
||||||
|
definitions = api.defined_names("""
|
||||||
|
x = y = None
|
||||||
|
""")
|
||||||
|
self.assertEqual([d.name for d in definitions],
|
||||||
|
['x', 'y'])
|
||||||
|
|
||||||
|
def test_multiple_imports(self):
|
||||||
|
definitions = api.defined_names("""
|
||||||
|
from module import a, b
|
||||||
|
from another_module import *
|
||||||
|
""")
|
||||||
|
self.assertEqual([d.name for d in definitions],
|
||||||
|
['a', 'b'])
|
||||||
|
|
||||||
|
def test_nested_definitions(self):
|
||||||
|
definitions = api.defined_names("""
|
||||||
|
class Class:
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
def g():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
self.assertEqual([d.name for d in definitions],
|
||||||
|
['Class'])
|
||||||
|
subdefinitions = definitions[0].defined_names()
|
||||||
|
self.assertEqual([d.name for d in subdefinitions],
|
||||||
|
['f', 'g'])
|
||||||
|
self.assertEqual([d.full_name for d in subdefinitions],
|
||||||
|
['Class.f', 'Class.g'])
|
||||||
|
|
||||||
|
|
||||||
class TestSpeed(TestBase):
|
class TestSpeed(TestBase):
|
||||||
def _check_speed(time_per_run, number=4, run_warm=True):
|
def _check_speed(time_per_run, number=4, run_warm=True):
|
||||||
""" Speed checks should typically be very tolerant. Some machines are
|
""" Speed checks should typically be very tolerant. Some machines are
|
||||||
|
|||||||
Reference in New Issue
Block a user