1
0
forked from VimPlug/jedi

Merge pull request #130 from tkf/list_definitions

Add new API: jedi.api.get_definitions
This commit is contained in:
David Halter
2013-03-10 12:56:08 -07:00
4 changed files with 139 additions and 1 deletions

View File

@@ -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):
"""

View File

@@ -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"""

View File

@@ -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)

View File

@@ -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