Index: tools/telemetry/third_party/rope/rope/contrib/codeassist.py |
diff --git a/tools/telemetry/third_party/rope/rope/contrib/codeassist.py b/tools/telemetry/third_party/rope/rope/contrib/codeassist.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..92c1bfc275c77c0b2985545426f736a5ed37dfde |
--- /dev/null |
+++ b/tools/telemetry/third_party/rope/rope/contrib/codeassist.py |
@@ -0,0 +1,695 @@ |
+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`\s |
+ |
+ `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`\s. |
+ |
+ `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) |