312 lines
10 KiB
Python
312 lines
10 KiB
Python
|
""" The classes returned by the api """
|
||
|
|
||
|
import re
|
||
|
import os
|
||
|
|
||
|
import settings
|
||
|
import evaluate
|
||
|
import imports
|
||
|
import parsing
|
||
|
import keywords
|
||
|
|
||
|
|
||
|
class BaseDefinition(object):
|
||
|
_mapping = {'posixpath': 'os.path',
|
||
|
'riscospath': 'os.path',
|
||
|
'ntpath': 'os.path',
|
||
|
'os2emxpath': 'os.path',
|
||
|
'macpath': 'os.path',
|
||
|
'genericpath': 'os.path',
|
||
|
'_io': 'io',
|
||
|
'__builtin__': '',
|
||
|
'builtins': '',
|
||
|
}
|
||
|
|
||
|
_tuple_mapping = dict((tuple(k.split('.')), v) for (k, v) in {
|
||
|
'argparse._ActionsContainer': 'argparse.ArgumentParser',
|
||
|
'_sre.SRE_Match': 're.MatchObject',
|
||
|
'_sre.SRE_Pattern': 're.RegexObject',
|
||
|
}.items())
|
||
|
|
||
|
def __init__(self, definition, start_pos):
|
||
|
self.start_pos = start_pos
|
||
|
self.definition = definition
|
||
|
self.is_keyword = isinstance(definition, keywords.Keyword)
|
||
|
|
||
|
# generate a path to the definition
|
||
|
self.module_path = str(definition.get_parent_until().path)
|
||
|
|
||
|
@property
|
||
|
def type(self):
|
||
|
# generate the type
|
||
|
stripped = self.definition
|
||
|
if isinstance(self.definition, evaluate.InstanceElement):
|
||
|
stripped = self.definition.var
|
||
|
return type(stripped).__name__
|
||
|
|
||
|
@property
|
||
|
def path(self):
|
||
|
path = []
|
||
|
if not isinstance(self.definition, keywords.Keyword):
|
||
|
par = self.definition
|
||
|
while par is not None:
|
||
|
try:
|
||
|
path.insert(0, par.name)
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
par = par.parent
|
||
|
return path
|
||
|
|
||
|
@property
|
||
|
def module_name(self):
|
||
|
path = self.module_path
|
||
|
sep = os.path.sep
|
||
|
p = re.sub(r'^.*?([\w\d]+)(%s__init__)?.py$' % sep, r'\1', path)
|
||
|
return p
|
||
|
|
||
|
def in_builtin_module(self):
|
||
|
return not self.module_path.endswith('.py')
|
||
|
|
||
|
@property
|
||
|
def line_nr(self):
|
||
|
return self.start_pos[0]
|
||
|
|
||
|
@property
|
||
|
def column(self):
|
||
|
return self.start_pos[1]
|
||
|
|
||
|
@property
|
||
|
def doc(self):
|
||
|
""" Return a document string for this completion object. """
|
||
|
try:
|
||
|
return self.definition.doc
|
||
|
except AttributeError:
|
||
|
return self.raw_doc
|
||
|
|
||
|
@property
|
||
|
def raw_doc(self):
|
||
|
""" Returns the raw docstring `__doc__` for any object """
|
||
|
try:
|
||
|
return str(self.definition.docstr)
|
||
|
except AttributeError:
|
||
|
return ''
|
||
|
|
||
|
@property
|
||
|
def description(self):
|
||
|
return str(self.definition)
|
||
|
|
||
|
@property
|
||
|
def full_name(self):
|
||
|
"""
|
||
|
Returns the path to a certain class/function, see #61.
|
||
|
"""
|
||
|
path = [str(p) for p in self.path]
|
||
|
# TODO add further checks, the mapping should only occur on stdlib.
|
||
|
try:
|
||
|
path[0] = self._mapping[path[0]]
|
||
|
except KeyError:
|
||
|
pass
|
||
|
for key, repl in self._tuple_mapping.items():
|
||
|
if tuple(path[:len(key)]) == key:
|
||
|
path = [repl] + path[len(key):]
|
||
|
|
||
|
return '.'.join(path if path[0] else path[1:])
|
||
|
|
||
|
def __repr__(self):
|
||
|
return "<%s %s>" % (type(self).__name__, self.description)
|
||
|
|
||
|
|
||
|
class Completion(BaseDefinition):
|
||
|
""" `Completion` objects are returned from `Script.complete`. Providing
|
||
|
some useful functions for IDE's. """
|
||
|
def __init__(self, name, needs_dot, like_name_length, base):
|
||
|
super(Completion, self).__init__(name.parent, name.start_pos)
|
||
|
|
||
|
self.name = name
|
||
|
self.needs_dot = needs_dot
|
||
|
self.like_name_length = like_name_length
|
||
|
self.base = base
|
||
|
|
||
|
self._followed_definitions = None
|
||
|
|
||
|
@property
|
||
|
def complete(self):
|
||
|
""" Delievers the rest of the word, e.g. completing `isinstance`
|
||
|
>>> isinstan
|
||
|
|
||
|
would return the string 'ce'. It also adds additional stuff, depending
|
||
|
on your `settings.py`
|
||
|
"""
|
||
|
dot = '.' if self.needs_dot else ''
|
||
|
append = ''
|
||
|
if settings.add_bracket_after_function \
|
||
|
and self.type == 'Function':
|
||
|
append = '('
|
||
|
|
||
|
if settings.add_dot_after_module:
|
||
|
if isinstance(self.base, parsing.Module):
|
||
|
append += '.'
|
||
|
if isinstance(self.base, parsing.Param):
|
||
|
append += '='
|
||
|
return dot + self.name.names[-1][self.like_name_length:] + append
|
||
|
|
||
|
@property
|
||
|
def word(self):
|
||
|
""" In contrary to `complete` returns the whole word, e.g.
|
||
|
>>> isinstan
|
||
|
|
||
|
would return 'isinstance'.
|
||
|
"""
|
||
|
return str(self.name.names[-1])
|
||
|
|
||
|
@property
|
||
|
def description(self):
|
||
|
""" Provides a description of the completion object
|
||
|
TODO return value is just __repr__ of some objects, improve! """
|
||
|
parent = self.name.parent
|
||
|
if parent is None:
|
||
|
return ''
|
||
|
t = self.type
|
||
|
if t == 'Statement' or t == 'Import':
|
||
|
desc = self.definition.get_code(False)
|
||
|
else:
|
||
|
desc = '.'.join(str(p) for p in self.path)
|
||
|
|
||
|
line_nr = '' if self.in_builtin_module else '@%s' % self.line_nr
|
||
|
return '%s: %s%s' % (t, desc, line_nr)
|
||
|
|
||
|
def follow_definition(self):
|
||
|
""" Returns you the original definitions. I strongly recommend not
|
||
|
using it for your completions, because it might slow down Jedi. If you
|
||
|
want to read only a few objects (<=20). I think it might be useful,
|
||
|
especially to get the original docstrings.
|
||
|
The basic problem of this function is that it follows all results. This
|
||
|
means with 1000 completions (e.g. numpy), it's just PITA slow.
|
||
|
"""
|
||
|
if self._followed_definitions is None:
|
||
|
if self.definition.isinstance(parsing.Statement):
|
||
|
defs = evaluate.follow_statement(self.definition)
|
||
|
elif self.definition.isinstance(parsing.Import):
|
||
|
defs = imports.strip_imports([self.definition])
|
||
|
else:
|
||
|
return [self]
|
||
|
|
||
|
self._followed_definitions = \
|
||
|
[BaseDefinition(d, d.start_pos) for d in defs]
|
||
|
evaluate.clear_caches()
|
||
|
|
||
|
return self._followed_definitions
|
||
|
|
||
|
def __repr__(self):
|
||
|
return '<%s: %s>' % (type(self).__name__, self.name)
|
||
|
|
||
|
|
||
|
class Definition(BaseDefinition):
|
||
|
""" These are the objects returned by either `Script.goto` or
|
||
|
`Script.get_definition`. """
|
||
|
def __init__(self, definition):
|
||
|
super(Definition, self).__init__(definition, definition.start_pos)
|
||
|
|
||
|
@property
|
||
|
def description(self):
|
||
|
""" A description of the Definition object, which is heavily used in
|
||
|
testing. e.g. for `isinstance` it returns 'def isinstance' """
|
||
|
d = self.definition
|
||
|
if isinstance(d, evaluate.InstanceElement):
|
||
|
d = d.var
|
||
|
if isinstance(d, evaluate.parsing.Name):
|
||
|
d = d.parent
|
||
|
|
||
|
if isinstance(d, evaluate.Array):
|
||
|
d = 'class ' + d.type
|
||
|
elif isinstance(d, (parsing.Class, evaluate.Class, evaluate.Instance)):
|
||
|
d = 'class ' + str(d.name)
|
||
|
elif isinstance(d, (evaluate.Function, evaluate.parsing.Function)):
|
||
|
d = 'def ' + str(d.name)
|
||
|
elif isinstance(d, evaluate.parsing.Module):
|
||
|
# only show module name
|
||
|
d = 'module %s' % self.module_name
|
||
|
elif self.is_keyword:
|
||
|
d = 'keyword %s' % d.name
|
||
|
else:
|
||
|
d = d.get_code().replace('\n', '')
|
||
|
return d
|
||
|
|
||
|
@property
|
||
|
def desc_with_module(self):
|
||
|
""" In addition to the Definition, it also returns the module. Don't
|
||
|
use it yet, its behaviour may change. If you really need it, talk to me
|
||
|
TODO add full path. This function is should return a
|
||
|
module.class.function path. """
|
||
|
if self.module_path.endswith('.py') \
|
||
|
and not isinstance(self.definition, parsing.Module):
|
||
|
position = '@%s' % (self.line_nr)
|
||
|
else:
|
||
|
# is a builtin or module
|
||
|
position = ''
|
||
|
return "%s:%s%s" % (self.module_name, self.description, position)
|
||
|
|
||
|
|
||
|
class RelatedName(BaseDefinition):
|
||
|
def __init__(self, name_part, scope):
|
||
|
super(RelatedName, self).__init__(scope, name_part.start_pos)
|
||
|
self.name_part = name_part
|
||
|
self.text = str(name_part)
|
||
|
self.end_pos = name_part.end_pos
|
||
|
|
||
|
@property
|
||
|
def description(self):
|
||
|
return "%s@%s,%s" % (self.text, self.start_pos[0], self.start_pos[1])
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
return self.start_pos == other.start_pos \
|
||
|
and self.module_path == other.module_path
|
||
|
|
||
|
def __hash__(self):
|
||
|
return hash((self.start_pos, self.module_path))
|
||
|
|
||
|
|
||
|
class CallDef(object):
|
||
|
""" `CallDef` objects is the return value of `Script.get_in_function_call`.
|
||
|
It knows what functions you are currently in. e.g. `isinstance(` would
|
||
|
return the `isinstance` function. without `(` it would return nothing."""
|
||
|
def __init__(self, executable, index, call):
|
||
|
self.executable = executable
|
||
|
self.index = index
|
||
|
self.call = call
|
||
|
|
||
|
@property
|
||
|
def params(self):
|
||
|
if self.executable.isinstance(evaluate.Function):
|
||
|
if isinstance(self.executable, evaluate.InstanceElement):
|
||
|
return self.executable.params[1:]
|
||
|
return self.executable.params
|
||
|
else:
|
||
|
try:
|
||
|
sub = self.executable.get_subscope_by_name('__init__')
|
||
|
return sub.params[1:] # ignore self
|
||
|
except KeyError:
|
||
|
return []
|
||
|
|
||
|
@property
|
||
|
def bracket_start(self):
|
||
|
""" The indent of the bracket that is responsible for the last function
|
||
|
call. """
|
||
|
c = self.call
|
||
|
while c.next is not None:
|
||
|
c = c.next
|
||
|
return c.name.end_pos
|
||
|
|
||
|
@property
|
||
|
def call_name(self):
|
||
|
""" The name (e.g. 'isinstance') as a string. """
|
||
|
return str(self.executable.name)
|
||
|
|
||
|
@property
|
||
|
def module(self):
|
||
|
return self.executable.get_parent_until()
|
||
|
|
||
|
def __repr__(self):
|
||
|
return '<%s: %s index %s>' % (type(self).__name__, self.executable,
|
||
|
self.index)
|