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()
|
||||
|
||||
|
||||
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,
|
||||
notices=True, speed=True):
|
||||
"""
|
||||
|
||||
@@ -8,7 +8,7 @@ import re
|
||||
import os
|
||||
import warnings
|
||||
|
||||
from _compatibility import unicode
|
||||
from _compatibility import unicode, next
|
||||
import cache
|
||||
import dynamic
|
||||
import recursion
|
||||
@@ -58,6 +58,9 @@ class BaseDefinition(object):
|
||||
def __init__(self, definition, start_pos):
|
||||
self.start_pos = start_pos
|
||||
self.definition = definition
|
||||
"""
|
||||
An instance of :class:`jedi.parsing_representation.Base` subclass.
|
||||
"""
|
||||
self.is_keyword = isinstance(definition, keywords.Keyword)
|
||||
|
||||
# generate a path to the definition
|
||||
@@ -271,6 +274,40 @@ class Definition(BaseDefinition):
|
||||
def __init__(self, definition):
|
||||
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
|
||||
def description(self):
|
||||
"""
|
||||
@@ -317,6 +354,32 @@ class Definition(BaseDefinition):
|
||||
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):
|
||||
"""TODO: document this"""
|
||||
|
||||
@@ -153,6 +153,8 @@ def get_names_of_scope(scope, position=None, star_search=True,
|
||||
>>> pairs[2] #doctest: +ELLIPSIS
|
||||
(<Module: ...builtin...>, [<Name: ...>, ...])
|
||||
|
||||
:rtype: [(pr.Scope, [pr.Name])]
|
||||
:return: Return an generator that yields a pair of scope and names.
|
||||
"""
|
||||
in_func_scope = scope
|
||||
non_flow = scope.get_parent_until(pr.Flow, reverse=True)
|
||||
|
||||
@@ -391,6 +391,60 @@ class TestFeature(TestBase):
|
||||
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):
|
||||
def _check_speed(time_per_run, number=4, run_warm=True):
|
||||
""" Speed checks should typically be very tolerant. Some machines are
|
||||
|
||||
Reference in New Issue
Block a user