741 lines
26 KiB
Python
741 lines
26 KiB
Python
import keyword
|
|
import sys
|
|
import warnings
|
|
|
|
import rope.base.codeanalyze
|
|
import rope.base.evaluate
|
|
from rope.base import builtins
|
|
from rope.base import exceptions
|
|
from rope.base import libutils
|
|
from rope.base import pynames
|
|
from rope.base import pynamesdef
|
|
from rope.base import pyobjects
|
|
from rope.base import pyobjectsdef
|
|
from rope.base import pyscopes
|
|
from rope.base import worder
|
|
from rope.contrib import fixsyntax
|
|
from rope.refactor import functionutils
|
|
|
|
|
|
def code_assist(
|
|
project,
|
|
source_code,
|
|
offset,
|
|
resource=None,
|
|
templates=None,
|
|
maxfixes=1,
|
|
later_locals=True,
|
|
):
|
|
"""Return python code completions as a list of `CodeAssistProposal`
|
|
|
|
`resource` is a `rope.base.resources.Resource` object. If
|
|
provided, relative imports are handled.
|
|
|
|
`maxfixes` is the maximum number of errors to fix if the code has
|
|
errors in it.
|
|
|
|
If `later_locals` is `False` names defined in this scope and after
|
|
this line is ignored.
|
|
|
|
"""
|
|
if templates is not None:
|
|
warnings.warn(
|
|
"Codeassist no longer supports templates", DeprecationWarning, stacklevel=2
|
|
)
|
|
assist = _PythonCodeAssist(
|
|
project,
|
|
source_code,
|
|
offset,
|
|
resource=resource,
|
|
maxfixes=maxfixes,
|
|
later_locals=later_locals,
|
|
)
|
|
return assist()
|
|
|
|
|
|
def starting_offset(source_code, offset):
|
|
"""Return the offset in which the completion should be inserted
|
|
|
|
Usually code assist proposals should be inserted like::
|
|
|
|
completion = proposal.name
|
|
result = (source_code[:starting_offset] +
|
|
completion + source_code[offset:])
|
|
|
|
Where starting_offset is the offset returned by this function.
|
|
|
|
"""
|
|
word_finder = worder.Worder(source_code, True)
|
|
expression, starting, starting_offset = word_finder.get_splitted_primary_before(
|
|
offset
|
|
)
|
|
return starting_offset
|
|
|
|
|
|
def get_doc(project, source_code, offset, resource=None, maxfixes=1):
|
|
"""Get the pydoc"""
|
|
fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes)
|
|
pyname = fixer.pyname_at(offset)
|
|
if pyname is None:
|
|
return None
|
|
pyobject = pyname.get_object()
|
|
return PyDocExtractor().get_doc(pyobject)
|
|
|
|
|
|
def get_calltip(
|
|
project,
|
|
source_code,
|
|
offset,
|
|
resource=None,
|
|
maxfixes=1,
|
|
ignore_unknown=False,
|
|
remove_self=False,
|
|
):
|
|
"""Get the calltip of a function
|
|
|
|
The format of the returned string is
|
|
``module_name.holding_scope_names.function_name(arguments)``. For
|
|
classes `__init__()` and for normal objects `__call__()` function
|
|
is used.
|
|
|
|
Note that the offset is on the function itself *not* after the its
|
|
open parenthesis. (Actually it used to be the other way but it
|
|
was easily confused when string literals were involved. So I
|
|
decided it is better for it not to try to be too clever when it
|
|
cannot be clever enough). You can use a simple search like::
|
|
|
|
offset = source_code.rindex('(', 0, offset) - 1
|
|
|
|
to handle simple situations.
|
|
|
|
If `ignore_unknown` is `True`, `None` is returned for functions
|
|
without source-code like builtins and extensions.
|
|
|
|
If `remove_self` is `True`, the first parameter whose name is self
|
|
will be removed for methods.
|
|
"""
|
|
fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes)
|
|
pyname = fixer.pyname_at(offset)
|
|
if pyname is None:
|
|
return None
|
|
pyobject = pyname.get_object()
|
|
return PyDocExtractor().get_calltip(pyobject, ignore_unknown, remove_self)
|
|
|
|
|
|
def get_definition_location(project, source_code, offset, resource=None, maxfixes=1):
|
|
"""Return the definition location of the python name at `offset`
|
|
|
|
Return a (`rope.base.resources.Resource`, lineno) tuple. If no
|
|
`resource` is given and the definition is inside the same module,
|
|
the first element of the returned tuple would be `None`. If the
|
|
location cannot be determined ``(None, None)`` is returned.
|
|
|
|
"""
|
|
fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes)
|
|
pyname = fixer.pyname_at(offset)
|
|
if pyname is not None:
|
|
module, lineno = pyname.get_definition_location()
|
|
if module is not None:
|
|
return module.get_module().get_resource(), lineno
|
|
return (None, None)
|
|
|
|
|
|
def find_occurrences(*args, **kwds):
|
|
import rope.contrib.findit
|
|
|
|
warnings.warn(
|
|
"Use `rope.contrib.findit.find_occurrences()` instead",
|
|
DeprecationWarning,
|
|
stacklevel=2,
|
|
)
|
|
return rope.contrib.findit.find_occurrences(*args, **kwds)
|
|
|
|
|
|
def get_canonical_path(project, resource, offset):
|
|
"""Get the canonical path to an object.
|
|
|
|
Given the offset of the object, this returns a list of
|
|
(name, name_type) tuples representing the canonical path to the
|
|
object. For example, the 'x' in the following code:
|
|
|
|
class Foo(object):
|
|
def bar(self):
|
|
class Qux(object):
|
|
def mux(self, x):
|
|
pass
|
|
|
|
we will return:
|
|
|
|
[('Foo', 'CLASS'), ('bar', 'FUNCTION'), ('Qux', 'CLASS'),
|
|
('mux', 'FUNCTION'), ('x', 'PARAMETER')]
|
|
|
|
`resource` is a `rope.base.resources.Resource` object.
|
|
|
|
`offset` is the offset of the pyname you want the path to.
|
|
|
|
"""
|
|
# Retrieve the PyName.
|
|
pymod = project.get_pymodule(resource)
|
|
pyname = rope.base.evaluate.eval_location(pymod, offset)
|
|
|
|
# Now get the location of the definition and its containing scope.
|
|
defmod, lineno = pyname.get_definition_location()
|
|
if not defmod:
|
|
return None
|
|
scope = defmod.get_scope().get_inner_scope_for_line(lineno)
|
|
|
|
# Start with the name of the object we're interested in.
|
|
names = []
|
|
if isinstance(pyname, pynamesdef.ParameterName):
|
|
names = [(worder.get_name_at(pymod.get_resource(), offset), "PARAMETER")]
|
|
elif isinstance(pyname, pynamesdef.AssignedName):
|
|
names = [(worder.get_name_at(pymod.get_resource(), offset), "VARIABLE")]
|
|
|
|
# Collect scope names.
|
|
while scope.parent:
|
|
if isinstance(scope, pyscopes.FunctionScope):
|
|
scope_type = "FUNCTION"
|
|
elif isinstance(scope, pyscopes.ClassScope):
|
|
scope_type = "CLASS"
|
|
else:
|
|
scope_type = None
|
|
names.append((scope.pyobject.get_name(), scope_type))
|
|
scope = scope.parent
|
|
|
|
names.append((defmod.get_resource().real_path, "MODULE"))
|
|
names.reverse()
|
|
return names
|
|
|
|
|
|
class CompletionProposal(object):
|
|
"""A completion proposal
|
|
|
|
The `scope` instance variable shows where proposed name came from
|
|
and can be 'global', 'local', 'builtin', 'attribute', 'keyword',
|
|
'imported', 'parameter_keyword'.
|
|
|
|
The `type` instance variable shows the approximate type of the
|
|
proposed object and can be 'instance', 'class', 'function', 'module',
|
|
and `None`.
|
|
|
|
All possible relations between proposal's `scope` and `type` are shown
|
|
in the table below (different scopes in rows and types in columns):
|
|
|
|
| instance | class | function | module | None
|
|
local | + | + | + | + |
|
|
global | + | + | + | + |
|
|
builtin | + | + | + | |
|
|
attribute | + | + | + | + |
|
|
imported | + | + | + | + |
|
|
keyword | | | | | +
|
|
parameter_keyword | | | | | +
|
|
|
|
"""
|
|
|
|
def __init__(self, name, scope, pyname=None):
|
|
self.name = name
|
|
self.pyname = pyname
|
|
self.scope = self._get_scope(scope)
|
|
|
|
def __str__(self):
|
|
return "%s (%s, %s)" % (self.name, self.scope, self.type)
|
|
|
|
def __repr__(self):
|
|
return str(self)
|
|
|
|
@property
|
|
def parameters(self):
|
|
"""The names of the parameters the function takes.
|
|
|
|
Returns None if this completion is not a function.
|
|
"""
|
|
pyname = self.pyname
|
|
if isinstance(pyname, pynames.ImportedName):
|
|
pyname = pyname._get_imported_pyname()
|
|
if isinstance(pyname, pynames.DefinedName):
|
|
pyobject = pyname.get_object()
|
|
if isinstance(pyobject, pyobjects.AbstractFunction):
|
|
return pyobject.get_param_names()
|
|
|
|
@property
|
|
def type(self):
|
|
pyname = self.pyname
|
|
if isinstance(pyname, builtins.BuiltinName):
|
|
pyobject = pyname.get_object()
|
|
if isinstance(pyobject, builtins.BuiltinFunction):
|
|
return "function"
|
|
elif isinstance(pyobject, builtins.BuiltinClass):
|
|
return "class"
|
|
elif isinstance(pyobject, builtins.BuiltinObject) or isinstance(
|
|
pyobject, builtins.BuiltinName
|
|
):
|
|
return "instance"
|
|
elif isinstance(pyname, pynames.ImportedModule):
|
|
return "module"
|
|
elif isinstance(pyname, pynames.ImportedName) or isinstance(
|
|
pyname, pynames.DefinedName
|
|
):
|
|
pyobject = pyname.get_object()
|
|
if isinstance(pyobject, pyobjects.AbstractFunction):
|
|
return "function"
|
|
if isinstance(pyobject, pyobjects.AbstractClass):
|
|
return "class"
|
|
return "instance"
|
|
|
|
def _get_scope(self, scope):
|
|
if isinstance(self.pyname, builtins.BuiltinName):
|
|
return "builtin"
|
|
if isinstance(self.pyname, pynames.ImportedModule) or isinstance(
|
|
self.pyname, pynames.ImportedName
|
|
):
|
|
return "imported"
|
|
return scope
|
|
|
|
def get_doc(self):
|
|
"""Get the proposed object's docstring.
|
|
|
|
Returns None if it can not be get.
|
|
"""
|
|
if not self.pyname:
|
|
return None
|
|
pyobject = self.pyname.get_object()
|
|
if not hasattr(pyobject, "get_doc"):
|
|
return None
|
|
return self.pyname.get_object().get_doc()
|
|
|
|
@property
|
|
def kind(self):
|
|
warnings.warn(
|
|
"the proposal's `kind` property is deprecated, " "use `scope` instead"
|
|
)
|
|
return self.scope
|
|
|
|
|
|
# leaved for backward compatibility
|
|
CodeAssistProposal = CompletionProposal
|
|
|
|
|
|
class NamedParamProposal(CompletionProposal):
|
|
"""A parameter keyword completion proposal
|
|
|
|
Holds reference to ``_function`` -- the function which
|
|
parameter ``name`` belongs to. This allows to determine
|
|
default value for this parameter.
|
|
"""
|
|
|
|
def __init__(self, name, function):
|
|
self.argname = name
|
|
name = "%s=" % name
|
|
super(NamedParamProposal, self).__init__(name, "parameter_keyword")
|
|
self._function = function
|
|
|
|
def get_default(self):
|
|
"""Get a string representation of a param's default value.
|
|
|
|
Returns None if there is no default value for this param.
|
|
"""
|
|
definfo = functionutils.DefinitionInfo.read(self._function)
|
|
for arg, default in definfo.args_with_defaults:
|
|
if self.argname == arg:
|
|
return default
|
|
return None
|
|
|
|
|
|
def sorted_proposals(proposals, scopepref=None, typepref=None):
|
|
"""Sort a list of proposals
|
|
|
|
Return a sorted list of the given `CodeAssistProposal`.
|
|
|
|
`scopepref` can be a list of proposal scopes. Defaults to
|
|
``['parameter_keyword', 'local', 'global', 'imported',
|
|
'attribute', 'builtin', 'keyword']``.
|
|
|
|
`typepref` can be a list of proposal types. Defaults to
|
|
``['class', 'function', 'instance', 'module', None]``.
|
|
(`None` stands for completions with no type like keywords.)
|
|
"""
|
|
sorter = _ProposalSorter(proposals, scopepref, typepref)
|
|
return sorter.get_sorted_proposal_list()
|
|
|
|
|
|
def starting_expression(source_code, offset):
|
|
"""Return the expression to complete"""
|
|
word_finder = worder.Worder(source_code, True)
|
|
expression, starting, starting_offset = word_finder.get_splitted_primary_before(
|
|
offset
|
|
)
|
|
if expression:
|
|
return expression + "." + starting
|
|
return starting
|
|
|
|
|
|
def default_templates():
|
|
warnings.warn(
|
|
"default_templates() is deprecated.", DeprecationWarning, stacklevel=2
|
|
)
|
|
return {}
|
|
|
|
|
|
class _PythonCodeAssist(object):
|
|
def __init__(
|
|
self, project, source_code, offset, resource=None, maxfixes=1, later_locals=True
|
|
):
|
|
self.project = project
|
|
self.code = source_code
|
|
self.resource = resource
|
|
self.maxfixes = maxfixes
|
|
self.later_locals = later_locals
|
|
self.word_finder = worder.Worder(source_code, True)
|
|
(
|
|
self.expression,
|
|
self.starting,
|
|
self.offset,
|
|
) = self.word_finder.get_splitted_primary_before(offset)
|
|
|
|
keywords = keyword.kwlist
|
|
|
|
def _find_starting_offset(self, source_code, offset):
|
|
current_offset = offset - 1
|
|
while current_offset >= 0 and (
|
|
source_code[current_offset].isalnum() or source_code[current_offset] in "_"
|
|
):
|
|
current_offset -= 1
|
|
return current_offset + 1
|
|
|
|
def _matching_keywords(self, starting):
|
|
result = []
|
|
for kw in self.keywords:
|
|
if kw.startswith(starting):
|
|
result.append(CompletionProposal(kw, "keyword"))
|
|
return result
|
|
|
|
def __call__(self):
|
|
if self.offset > len(self.code):
|
|
return []
|
|
completions = list(self._code_completions().values())
|
|
if self.expression.strip() == "" and self.starting.strip() != "":
|
|
completions.extend(self._matching_keywords(self.starting))
|
|
return completions
|
|
|
|
def _dotted_completions(self, module_scope, holding_scope):
|
|
result = {}
|
|
found_pyname = rope.base.evaluate.eval_str(holding_scope, self.expression)
|
|
if found_pyname is not None:
|
|
element = found_pyname.get_object()
|
|
compl_scope = "attribute"
|
|
if isinstance(element, (pyobjectsdef.PyModule, pyobjectsdef.PyPackage)):
|
|
compl_scope = "imported"
|
|
for name, pyname in element.get_attributes().items():
|
|
if name.startswith(self.starting):
|
|
result[name] = CompletionProposal(name, compl_scope, pyname)
|
|
return result
|
|
|
|
def _undotted_completions(self, scope, result, lineno=None):
|
|
if scope.parent is not None:
|
|
self._undotted_completions(scope.parent, result)
|
|
if lineno is None:
|
|
names = scope.get_propagated_names()
|
|
else:
|
|
names = scope.get_names()
|
|
for name, pyname in names.items():
|
|
if name.startswith(self.starting):
|
|
compl_scope = "local"
|
|
if scope.get_kind() == "Module":
|
|
compl_scope = "global"
|
|
if (
|
|
lineno is None
|
|
or self.later_locals
|
|
or not self._is_defined_after(scope, pyname, lineno)
|
|
):
|
|
result[name] = CompletionProposal(name, compl_scope, pyname)
|
|
|
|
def _from_import_completions(self, pymodule):
|
|
module_name = self.word_finder.get_from_module(self.offset)
|
|
if module_name is None:
|
|
return {}
|
|
pymodule = self._find_module(pymodule, module_name)
|
|
result = {}
|
|
for name in pymodule:
|
|
if name.startswith(self.starting):
|
|
result[name] = CompletionProposal(
|
|
name, scope="global", pyname=pymodule[name]
|
|
)
|
|
return result
|
|
|
|
def _find_module(self, pymodule, module_name):
|
|
dots = 0
|
|
while module_name[dots] == ".":
|
|
dots += 1
|
|
pyname = pynames.ImportedModule(pymodule, module_name[dots:], dots)
|
|
return pyname.get_object()
|
|
|
|
def _is_defined_after(self, scope, pyname, lineno):
|
|
location = pyname.get_definition_location()
|
|
if location is not None and location[1] is not None:
|
|
if (
|
|
location[0] == scope.pyobject.get_module()
|
|
and lineno <= location[1] <= scope.get_end()
|
|
):
|
|
return True
|
|
|
|
def _code_completions(self):
|
|
lineno = self.code.count("\n", 0, self.offset) + 1
|
|
fixer = fixsyntax.FixSyntax(
|
|
self.project, self.code, self.resource, self.maxfixes
|
|
)
|
|
pymodule = fixer.get_pymodule()
|
|
module_scope = pymodule.get_scope()
|
|
code = pymodule.source_code
|
|
lines = code.split("\n")
|
|
result = {}
|
|
start = fixsyntax._logical_start(lines, lineno)
|
|
indents = fixsyntax._get_line_indents(lines[start - 1])
|
|
inner_scope = module_scope.get_inner_scope_for_line(start, indents)
|
|
if self.word_finder.is_a_name_after_from_import(self.offset):
|
|
return self._from_import_completions(pymodule)
|
|
if self.expression.strip() != "":
|
|
result.update(self._dotted_completions(module_scope, inner_scope))
|
|
else:
|
|
result.update(self._keyword_parameters(module_scope.pyobject, inner_scope))
|
|
self._undotted_completions(inner_scope, result, lineno=lineno)
|
|
return result
|
|
|
|
def _keyword_parameters(self, pymodule, scope):
|
|
offset = self.offset
|
|
if offset == 0:
|
|
return {}
|
|
word_finder = worder.Worder(self.code, True)
|
|
if word_finder.is_on_function_call_keyword(offset - 1):
|
|
function_parens = word_finder.find_parens_start_from_inside(offset - 1)
|
|
primary = word_finder.get_primary_at(function_parens - 1)
|
|
try:
|
|
function_pyname = rope.base.evaluate.eval_str(scope, primary)
|
|
except exceptions.BadIdentifierError:
|
|
return {}
|
|
if function_pyname is not None:
|
|
pyobject = function_pyname.get_object()
|
|
if isinstance(pyobject, pyobjects.AbstractFunction):
|
|
pass
|
|
elif (
|
|
isinstance(pyobject, pyobjects.AbstractClass)
|
|
and "__init__" in pyobject
|
|
):
|
|
pyobject = pyobject["__init__"].get_object()
|
|
elif "__call__" in pyobject:
|
|
pyobject = pyobject["__call__"].get_object()
|
|
if isinstance(pyobject, pyobjects.AbstractFunction):
|
|
param_names = []
|
|
param_names.extend(pyobject.get_param_names(special_args=False))
|
|
result = {}
|
|
for name in param_names:
|
|
if name.startswith(self.starting):
|
|
result[name + "="] = NamedParamProposal(name, pyobject)
|
|
return result
|
|
return {}
|
|
|
|
|
|
class _ProposalSorter(object):
|
|
"""Sort a list of code assist proposals"""
|
|
|
|
def __init__(self, code_assist_proposals, scopepref=None, typepref=None):
|
|
self.proposals = code_assist_proposals
|
|
if scopepref is None:
|
|
scopepref = [
|
|
"parameter_keyword",
|
|
"local",
|
|
"global",
|
|
"imported",
|
|
"attribute",
|
|
"builtin",
|
|
"keyword",
|
|
]
|
|
self.scopepref = scopepref
|
|
if typepref is None:
|
|
typepref = ["class", "function", "instance", "module", None]
|
|
self.typerank = dict((type, index) for index, type in enumerate(typepref))
|
|
|
|
def get_sorted_proposal_list(self):
|
|
"""Return a list of `CodeAssistProposal`"""
|
|
proposals = {}
|
|
for proposal in self.proposals:
|
|
proposals.setdefault(proposal.scope, []).append(proposal)
|
|
result = []
|
|
for scope in self.scopepref:
|
|
scope_proposals = proposals.get(scope, [])
|
|
scope_proposals = [
|
|
proposal
|
|
for proposal in scope_proposals
|
|
if proposal.type in self.typerank
|
|
]
|
|
scope_proposals.sort(key=self._proposal_key)
|
|
result.extend(scope_proposals)
|
|
return result
|
|
|
|
def _proposal_key(self, proposal1):
|
|
def _underline_count(name):
|
|
return sum(1 for c in name if c == "_")
|
|
|
|
return (
|
|
self.typerank.get(proposal1.type, 100),
|
|
_underline_count(proposal1.name),
|
|
proposal1.name,
|
|
)
|
|
# if proposal1.type != proposal2.type:
|
|
# return cmp(self.typerank.get(proposal1.type, 100),
|
|
# self.typerank.get(proposal2.type, 100))
|
|
# return self._compare_underlined_names(proposal1.name,
|
|
# proposal2.name)
|
|
|
|
|
|
class PyDocExtractor(object):
|
|
def get_doc(self, pyobject):
|
|
if isinstance(pyobject, pyobjects.AbstractFunction):
|
|
return self._get_function_docstring(pyobject)
|
|
elif isinstance(pyobject, pyobjects.AbstractClass):
|
|
return self._get_class_docstring(pyobject)
|
|
elif isinstance(pyobject, pyobjects.AbstractModule):
|
|
return self._trim_docstring(pyobject.get_doc())
|
|
return None
|
|
|
|
def get_calltip(self, pyobject, ignore_unknown=False, remove_self=False):
|
|
try:
|
|
if isinstance(pyobject, pyobjects.AbstractClass):
|
|
pyobject = pyobject["__init__"].get_object()
|
|
if not isinstance(pyobject, pyobjects.AbstractFunction):
|
|
pyobject = pyobject["__call__"].get_object()
|
|
except exceptions.AttributeNotFoundError:
|
|
return None
|
|
if ignore_unknown and not isinstance(pyobject, pyobjects.PyFunction):
|
|
return
|
|
if isinstance(pyobject, pyobjects.AbstractFunction):
|
|
result = self._get_function_signature(pyobject, add_module=True)
|
|
if remove_self and self._is_method(pyobject):
|
|
return result.replace("(self)", "()").replace("(self, ", "(")
|
|
return result
|
|
|
|
def _get_class_docstring(self, pyclass):
|
|
contents = self._trim_docstring(pyclass.get_doc(), 2)
|
|
supers = [super.get_name() for super in pyclass.get_superclasses()]
|
|
doc = "class %s(%s):\n\n" % (pyclass.get_name(), ", ".join(supers)) + contents
|
|
|
|
if "__init__" in pyclass:
|
|
init = pyclass["__init__"].get_object()
|
|
if isinstance(init, pyobjects.AbstractFunction):
|
|
doc += "\n\n" + self._get_single_function_docstring(init)
|
|
return doc
|
|
|
|
def _get_function_docstring(self, pyfunction):
|
|
functions = [pyfunction]
|
|
if self._is_method(pyfunction):
|
|
functions.extend(
|
|
self._get_super_methods(pyfunction.parent, pyfunction.get_name())
|
|
)
|
|
return "\n\n".join(
|
|
[self._get_single_function_docstring(function) for function in functions]
|
|
)
|
|
|
|
def _is_method(self, pyfunction):
|
|
return isinstance(pyfunction, pyobjects.PyFunction) and isinstance(
|
|
pyfunction.parent, pyobjects.PyClass
|
|
)
|
|
|
|
def _get_single_function_docstring(self, pyfunction):
|
|
signature = self._get_function_signature(pyfunction)
|
|
docs = self._trim_docstring(pyfunction.get_doc(), indents=2)
|
|
return signature + ":\n\n" + docs
|
|
|
|
def _get_super_methods(self, pyclass, name):
|
|
result = []
|
|
for super_class in pyclass.get_superclasses():
|
|
if name in super_class:
|
|
function = super_class[name].get_object()
|
|
if isinstance(function, pyobjects.AbstractFunction):
|
|
result.append(function)
|
|
result.extend(self._get_super_methods(super_class, name))
|
|
return result
|
|
|
|
def _get_function_signature(self, pyfunction, add_module=False):
|
|
location = self._location(pyfunction, add_module)
|
|
if isinstance(pyfunction, pyobjects.PyFunction):
|
|
info = functionutils.DefinitionInfo.read(pyfunction)
|
|
return location + info.to_string()
|
|
else:
|
|
return "%s(%s)" % (
|
|
location + pyfunction.get_name(),
|
|
", ".join(pyfunction.get_param_names()),
|
|
)
|
|
|
|
def _location(self, pyobject, add_module=False):
|
|
location = []
|
|
parent = pyobject.parent
|
|
while parent and not isinstance(parent, pyobjects.AbstractModule):
|
|
location.append(parent.get_name())
|
|
location.append(".")
|
|
parent = parent.parent
|
|
if add_module:
|
|
if isinstance(pyobject, pyobjects.PyFunction):
|
|
location.insert(0, self._get_module(pyobject))
|
|
if isinstance(parent, builtins.BuiltinModule):
|
|
location.insert(0, parent.get_name() + ".")
|
|
return "".join(location)
|
|
|
|
def _get_module(self, pyfunction):
|
|
module = pyfunction.get_module()
|
|
if module is not None:
|
|
resource = module.get_resource()
|
|
if resource is not None:
|
|
return libutils.modname(resource) + "."
|
|
return ""
|
|
|
|
def _trim_docstring(self, docstring, indents=0):
|
|
"""The sample code from :PEP:`257`"""
|
|
if not docstring:
|
|
return ""
|
|
# Convert tabs to spaces (following normal Python rules)
|
|
# and split into a list of lines:
|
|
lines = docstring.expandtabs().splitlines()
|
|
# Determine minimum indentation (first line doesn't count):
|
|
indent = sys.maxsize
|
|
for line in lines[1:]:
|
|
stripped = line.lstrip()
|
|
if stripped:
|
|
indent = min(indent, len(line) - len(stripped))
|
|
# Remove indentation (first line is special):
|
|
trimmed = [lines[0].strip()]
|
|
if indent < sys.maxsize:
|
|
for line in lines[1:]:
|
|
trimmed.append(line[indent:].rstrip())
|
|
# Strip off trailing and leading blank lines:
|
|
while trimmed and not trimmed[-1]:
|
|
trimmed.pop()
|
|
while trimmed and not trimmed[0]:
|
|
trimmed.pop(0)
|
|
# Return a single string:
|
|
return "\n".join((" " * indents + line for line in trimmed))
|
|
|
|
|
|
# Deprecated classes
|
|
|
|
|
|
class TemplateProposal(CodeAssistProposal):
|
|
def __init__(self, name, template):
|
|
warnings.warn(
|
|
"TemplateProposal is deprecated.", DeprecationWarning, stacklevel=2
|
|
)
|
|
super(TemplateProposal, self).__init__(name, "template")
|
|
self.template = template
|
|
|
|
|
|
class Template(object):
|
|
def __init__(self, template):
|
|
self.template = template
|
|
warnings.warn("Template is deprecated.", DeprecationWarning, stacklevel=2)
|
|
|
|
def variables(self):
|
|
return []
|
|
|
|
def substitute(self, mapping):
|
|
return self.template
|
|
|
|
def get_cursor_location(self, mapping):
|
|
return len(self.template)
|