init
This commit is contained in:
commit
38355d2442
9083 changed files with 1225834 additions and 0 deletions
741
.venv/lib/python3.8/site-packages/rope/contrib/codeassist.py
Normal file
741
.venv/lib/python3.8/site-packages/rope/contrib/codeassist.py
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue