codeassist.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. import keyword
  2. import sys
  3. import warnings
  4. import rope.base.codeanalyze
  5. import rope.base.evaluate
  6. from rope.base import pyobjects, pyobjectsdef, pynames, builtins, exceptions, worder
  7. from rope.base.codeanalyze import SourceLinesAdapter
  8. from rope.contrib import fixsyntax
  9. from rope.refactor import functionutils
  10. def code_assist(project, source_code, offset, resource=None,
  11. templates=None, maxfixes=1, later_locals=True):
  12. """Return python code completions as a list of `CodeAssistProposal`\s
  13. `resource` is a `rope.base.resources.Resource` object. If
  14. provided, relative imports are handled.
  15. `maxfixes` is the maximum number of errors to fix if the code has
  16. errors in it.
  17. If `later_locals` is `False` names defined in this scope and after
  18. this line is ignored.
  19. """
  20. if templates is not None:
  21. warnings.warn('Codeassist no longer supports templates',
  22. DeprecationWarning, stacklevel=2)
  23. assist = _PythonCodeAssist(
  24. project, source_code, offset, resource=resource,
  25. maxfixes=maxfixes, later_locals=later_locals)
  26. return assist()
  27. def starting_offset(source_code, offset):
  28. """Return the offset in which the completion should be inserted
  29. Usually code assist proposals should be inserted like::
  30. completion = proposal.name
  31. result = (source_code[:starting_offset] +
  32. completion + source_code[offset:])
  33. Where starting_offset is the offset returned by this function.
  34. """
  35. word_finder = worder.Worder(source_code, True)
  36. expression, starting, starting_offset = \
  37. word_finder.get_splitted_primary_before(offset)
  38. return starting_offset
  39. def get_doc(project, source_code, offset, resource=None, maxfixes=1):
  40. """Get the pydoc"""
  41. fixer = fixsyntax.FixSyntax(project.pycore, source_code,
  42. resource, maxfixes)
  43. pymodule = fixer.get_pymodule()
  44. pyname = fixer.pyname_at(offset)
  45. if pyname is None:
  46. return None
  47. pyobject = pyname.get_object()
  48. return PyDocExtractor().get_doc(pyobject)
  49. def get_calltip(project, source_code, offset, resource=None,
  50. maxfixes=1, ignore_unknown=False, remove_self=False):
  51. """Get the calltip of a function
  52. The format of the returned string is
  53. ``module_name.holding_scope_names.function_name(arguments)``. For
  54. classes `__init__()` and for normal objects `__call__()` function
  55. is used.
  56. Note that the offset is on the function itself *not* after the its
  57. open parenthesis. (Actually it used to be the other way but it
  58. was easily confused when string literals were involved. So I
  59. decided it is better for it not to try to be too clever when it
  60. cannot be clever enough). You can use a simple search like::
  61. offset = source_code.rindex('(', 0, offset) - 1
  62. to handle simple situations.
  63. If `ignore_unknown` is `True`, `None` is returned for functions
  64. without source-code like builtins and extensions.
  65. If `remove_self` is `True`, the first parameter whose name is self
  66. will be removed for methods.
  67. """
  68. fixer = fixsyntax.FixSyntax(project.pycore, source_code,
  69. resource, maxfixes)
  70. pymodule = fixer.get_pymodule()
  71. pyname = fixer.pyname_at(offset)
  72. if pyname is None:
  73. return None
  74. pyobject = pyname.get_object()
  75. return PyDocExtractor().get_calltip(pyobject, ignore_unknown, remove_self)
  76. def get_definition_location(project, source_code, offset,
  77. resource=None, maxfixes=1):
  78. """Return the definition location of the python name at `offset`
  79. Return a (`rope.base.resources.Resource`, lineno) tuple. If no
  80. `resource` is given and the definition is inside the same module,
  81. the first element of the returned tuple would be `None`. If the
  82. location cannot be determined ``(None, None)`` is returned.
  83. """
  84. fixer = fixsyntax.FixSyntax(project.pycore, source_code,
  85. resource, maxfixes)
  86. pymodule = fixer.get_pymodule()
  87. pyname = fixer.pyname_at(offset)
  88. if pyname is not None:
  89. module, lineno = pyname.get_definition_location()
  90. if module is not None:
  91. return module.get_module().get_resource(), lineno
  92. return (None, None)
  93. def find_occurrences(*args, **kwds):
  94. import rope.contrib.findit
  95. warnings.warn('Use `rope.contrib.findit.find_occurrences()` instead',
  96. DeprecationWarning, stacklevel=2)
  97. return rope.contrib.findit.find_occurrences(*args, **kwds)
  98. class CompletionProposal(object):
  99. """A completion proposal
  100. The `scope` instance variable shows where proposed name came from
  101. and can be 'global', 'local', 'builtin', 'attribute', 'keyword',
  102. 'imported', 'parameter_keyword'.
  103. The `type` instance variable shows the approximate type of the
  104. proposed object and can be 'instance', 'class', 'function', 'module',
  105. and `None`.
  106. All possible relations between proposal's `scope` and `type` are shown
  107. in the table below (different scopes in rows and types in columns):
  108. | instance | class | function | module | None
  109. local | + | + | + | + |
  110. global | + | + | + | + |
  111. builtin | + | + | + | |
  112. attribute | + | + | + | + |
  113. imported | + | + | + | + |
  114. keyword | | | | | +
  115. parameter_keyword | | | | | +
  116. """
  117. def __init__(self, name, scope, pyname=None):
  118. self.name = name
  119. self.pyname = pyname
  120. self.scope = self._get_scope(scope)
  121. def __str__(self):
  122. return '%s (%s, %s)' % (self.name, self.scope, self.type)
  123. def __repr__(self):
  124. return str(self)
  125. @property
  126. def parameters(self):
  127. """The names of the parameters the function takes.
  128. Returns None if this completion is not a function.
  129. """
  130. pyname = self.pyname
  131. if isinstance(pyname, pynames.ImportedName):
  132. pyname = pyname._get_imported_pyname()
  133. if isinstance(pyname, pynames.DefinedName):
  134. pyobject = pyname.get_object()
  135. if isinstance(pyobject, pyobjects.AbstractFunction):
  136. return pyobject.get_param_names()
  137. @property
  138. def type(self):
  139. pyname = self.pyname
  140. if isinstance(pyname, builtins.BuiltinName):
  141. pyobject = pyname.get_object()
  142. if isinstance(pyobject, builtins.BuiltinFunction):
  143. return 'function'
  144. elif isinstance(pyobject, builtins.BuiltinClass):
  145. clsobj = pyobject.builtin
  146. return 'class'
  147. elif isinstance(pyobject, builtins.BuiltinObject) or \
  148. isinstance(pyobject, builtins.BuiltinName):
  149. return 'instance'
  150. elif isinstance(pyname, pynames.ImportedModule):
  151. return 'module'
  152. elif isinstance(pyname, pynames.ImportedName) or \
  153. isinstance(pyname, pynames.DefinedName):
  154. pyobject = pyname.get_object()
  155. if isinstance(pyobject, pyobjects.AbstractFunction):
  156. return 'function'
  157. if isinstance(pyobject, pyobjects.AbstractClass):
  158. return 'class'
  159. return 'instance'
  160. def _get_scope(self, scope):
  161. if isinstance(self.pyname, builtins.BuiltinName):
  162. return 'builtin'
  163. if isinstance(self.pyname, pynames.ImportedModule) or \
  164. isinstance(self.pyname, pynames.ImportedName):
  165. return 'imported'
  166. return scope
  167. def get_doc(self):
  168. """Get the proposed object's docstring.
  169. Returns None if it can not be get.
  170. """
  171. if not self.pyname:
  172. return None
  173. pyobject = self.pyname.get_object()
  174. if not hasattr(pyobject, 'get_doc'):
  175. return None
  176. return self.pyname.get_object().get_doc()
  177. @property
  178. def kind(self):
  179. warnings.warn("the proposal's `kind` property is deprecated, " \
  180. "use `scope` instead")
  181. return self.scope
  182. # leaved for backward compatibility
  183. CodeAssistProposal = CompletionProposal
  184. class NamedParamProposal(CompletionProposal):
  185. """A parameter keyword completion proposal
  186. Holds reference to ``_function`` -- the function which
  187. parameter ``name`` belongs to. This allows to determine
  188. default value for this parameter.
  189. """
  190. def __init__(self, name, function):
  191. self.argname = name
  192. name = '%s=' % name
  193. super(NamedParamProposal, self).__init__(name, 'parameter_keyword')
  194. self._function = function
  195. def get_default(self):
  196. """Get a string representation of a param's default value.
  197. Returns None if there is no default value for this param.
  198. """
  199. definfo = functionutils.DefinitionInfo.read(self._function)
  200. for arg, default in definfo.args_with_defaults:
  201. if self.argname == arg:
  202. return default
  203. return None
  204. def sorted_proposals(proposals, scopepref=None, typepref=None):
  205. """Sort a list of proposals
  206. Return a sorted list of the given `CodeAssistProposal`\s.
  207. `scopepref` can be a list of proposal scopes. Defaults to
  208. ``['parameter_keyword', 'local', 'global', 'imported',
  209. 'attribute', 'builtin', 'keyword']``.
  210. `typepref` can be a list of proposal types. Defaults to
  211. ``['class', 'function', 'instance', 'module', None]``.
  212. (`None` stands for completions with no type like keywords.)
  213. """
  214. sorter = _ProposalSorter(proposals, scopepref, typepref)
  215. return sorter.get_sorted_proposal_list()
  216. def starting_expression(source_code, offset):
  217. """Return the expression to complete"""
  218. word_finder = worder.Worder(source_code, True)
  219. expression, starting, starting_offset = \
  220. word_finder.get_splitted_primary_before(offset)
  221. if expression:
  222. return expression + '.' + starting
  223. return starting
  224. def default_templates():
  225. warnings.warn('default_templates() is deprecated.',
  226. DeprecationWarning, stacklevel=2)
  227. return {}
  228. class _PythonCodeAssist(object):
  229. def __init__(self, project, source_code, offset, resource=None,
  230. maxfixes=1, later_locals=True):
  231. self.project = project
  232. self.pycore = self.project.pycore
  233. self.code = source_code
  234. self.resource = resource
  235. self.maxfixes = maxfixes
  236. self.later_locals = later_locals
  237. self.word_finder = worder.Worder(source_code, True)
  238. self.expression, self.starting, self.offset = \
  239. self.word_finder.get_splitted_primary_before(offset)
  240. keywords = keyword.kwlist
  241. def _find_starting_offset(self, source_code, offset):
  242. current_offset = offset - 1
  243. while current_offset >= 0 and (source_code[current_offset].isalnum() or
  244. source_code[current_offset] in '_'):
  245. current_offset -= 1;
  246. return current_offset + 1
  247. def _matching_keywords(self, starting):
  248. result = []
  249. for kw in self.keywords:
  250. if kw.startswith(starting):
  251. result.append(CompletionProposal(kw, 'keyword'))
  252. return result
  253. def __call__(self):
  254. if self.offset > len(self.code):
  255. return []
  256. completions = list(self._code_completions().values())
  257. if self.expression.strip() == '' and self.starting.strip() != '':
  258. completions.extend(self._matching_keywords(self.starting))
  259. return completions
  260. def _dotted_completions(self, module_scope, holding_scope):
  261. result = {}
  262. found_pyname = rope.base.evaluate.eval_str(holding_scope,
  263. self.expression)
  264. if found_pyname is not None:
  265. element = found_pyname.get_object()
  266. compl_scope = 'attribute'
  267. if isinstance(element, (pyobjectsdef.PyModule,
  268. pyobjectsdef.PyPackage)):
  269. compl_scope = 'imported'
  270. for name, pyname in element.get_attributes().items():
  271. if name.startswith(self.starting):
  272. result[name] = CompletionProposal(name, compl_scope, pyname)
  273. return result
  274. def _undotted_completions(self, scope, result, lineno=None):
  275. if scope.parent != None:
  276. self._undotted_completions(scope.parent, result)
  277. if lineno is None:
  278. names = scope.get_propagated_names()
  279. else:
  280. names = scope.get_names()
  281. for name, pyname in names.items():
  282. if name.startswith(self.starting):
  283. compl_scope = 'local'
  284. if scope.get_kind() == 'Module':
  285. compl_scope = 'global'
  286. if lineno is None or self.later_locals or \
  287. not self._is_defined_after(scope, pyname, lineno):
  288. result[name] = CompletionProposal(name, compl_scope,
  289. pyname)
  290. def _from_import_completions(self, pymodule):
  291. module_name = self.word_finder.get_from_module(self.offset)
  292. if module_name is None:
  293. return {}
  294. pymodule = self._find_module(pymodule, module_name)
  295. result = {}
  296. for name in pymodule:
  297. if name.startswith(self.starting):
  298. result[name] = CompletionProposal(name, scope='global',
  299. pyname=pymodule[name])
  300. return result
  301. def _find_module(self, pymodule, module_name):
  302. dots = 0
  303. while module_name[dots] == '.':
  304. dots += 1
  305. pyname = pynames.ImportedModule(pymodule,
  306. module_name[dots:], dots)
  307. return pyname.get_object()
  308. def _is_defined_after(self, scope, pyname, lineno):
  309. location = pyname.get_definition_location()
  310. if location is not None and location[1] is not None:
  311. if location[0] == scope.pyobject.get_module() and \
  312. lineno <= location[1] <= scope.get_end():
  313. return True
  314. def _code_completions(self):
  315. lineno = self.code.count('\n', 0, self.offset) + 1
  316. fixer = fixsyntax.FixSyntax(self.pycore, self.code,
  317. self.resource, self.maxfixes)
  318. pymodule = fixer.get_pymodule()
  319. module_scope = pymodule.get_scope()
  320. code = pymodule.source_code
  321. lines = code.split('\n')
  322. result = {}
  323. start = fixsyntax._logical_start(lines, lineno)
  324. indents = fixsyntax._get_line_indents(lines[start - 1])
  325. inner_scope = module_scope.get_inner_scope_for_line(start, indents)
  326. if self.word_finder.is_a_name_after_from_import(self.offset):
  327. return self._from_import_completions(pymodule)
  328. if self.expression.strip() != '':
  329. result.update(self._dotted_completions(module_scope, inner_scope))
  330. else:
  331. result.update(self._keyword_parameters(module_scope.pyobject,
  332. inner_scope))
  333. self._undotted_completions(inner_scope, result, lineno=lineno)
  334. return result
  335. def _keyword_parameters(self, pymodule, scope):
  336. offset = self.offset
  337. if offset == 0:
  338. return {}
  339. word_finder = worder.Worder(self.code, True)
  340. lines = SourceLinesAdapter(self.code)
  341. lineno = lines.get_line_number(offset)
  342. if word_finder.is_on_function_call_keyword(offset - 1):
  343. name_finder = rope.base.evaluate.ScopeNameFinder(pymodule)
  344. function_parens = word_finder.\
  345. find_parens_start_from_inside(offset - 1)
  346. primary = word_finder.get_primary_at(function_parens - 1)
  347. try:
  348. function_pyname = rope.base.evaluate.\
  349. eval_str(scope, primary)
  350. except exceptions.BadIdentifierError, e:
  351. return {}
  352. if function_pyname is not None:
  353. pyobject = function_pyname.get_object()
  354. if isinstance(pyobject, pyobjects.AbstractFunction):
  355. pass
  356. elif isinstance(pyobject, pyobjects.AbstractClass) and \
  357. '__init__' in pyobject:
  358. pyobject = pyobject['__init__'].get_object()
  359. elif '__call__' in pyobject:
  360. pyobject = pyobject['__call__'].get_object()
  361. if isinstance(pyobject, pyobjects.AbstractFunction):
  362. param_names = []
  363. param_names.extend(
  364. pyobject.get_param_names(special_args=False))
  365. result = {}
  366. for name in param_names:
  367. if name.startswith(self.starting):
  368. result[name + '='] = NamedParamProposal(
  369. name, pyobject
  370. )
  371. return result
  372. return {}
  373. class _ProposalSorter(object):
  374. """Sort a list of code assist proposals"""
  375. def __init__(self, code_assist_proposals, scopepref=None, typepref=None):
  376. self.proposals = code_assist_proposals
  377. if scopepref is None:
  378. scopepref = ['parameter_keyword', 'local', 'global', 'imported',
  379. 'attribute', 'builtin', 'keyword']
  380. self.scopepref = scopepref
  381. if typepref is None:
  382. typepref = ['class', 'function', 'instance', 'module', None]
  383. self.typerank = dict((type, index)
  384. for index, type in enumerate(typepref))
  385. def get_sorted_proposal_list(self):
  386. """Return a list of `CodeAssistProposal`"""
  387. proposals = {}
  388. for proposal in self.proposals:
  389. proposals.setdefault(proposal.scope, []).append(proposal)
  390. result = []
  391. for scope in self.scopepref:
  392. scope_proposals = proposals.get(scope, [])
  393. scope_proposals = [proposal for proposal in scope_proposals
  394. if proposal.type in self.typerank]
  395. scope_proposals.sort(self._proposal_cmp)
  396. result.extend(scope_proposals)
  397. return result
  398. def _proposal_cmp(self, proposal1, proposal2):
  399. if proposal1.type != proposal2.type:
  400. return cmp(self.typerank.get(proposal1.type, 100),
  401. self.typerank.get(proposal2.type, 100))
  402. return self._compare_underlined_names(proposal1.name,
  403. proposal2.name)
  404. def _compare_underlined_names(self, name1, name2):
  405. def underline_count(name):
  406. result = 0
  407. while result < len(name) and name[result] == '_':
  408. result += 1
  409. return result
  410. underline_count1 = underline_count(name1)
  411. underline_count2 = underline_count(name2)
  412. if underline_count1 != underline_count2:
  413. return cmp(underline_count1, underline_count2)
  414. return cmp(name1, name2)
  415. class PyDocExtractor(object):
  416. def get_doc(self, pyobject):
  417. if isinstance(pyobject, pyobjects.AbstractFunction):
  418. return self._get_function_docstring(pyobject)
  419. elif isinstance(pyobject, pyobjects.AbstractClass):
  420. return self._get_class_docstring(pyobject)
  421. elif isinstance(pyobject, pyobjects.AbstractModule):
  422. return self._trim_docstring(pyobject.get_doc())
  423. return None
  424. def get_calltip(self, pyobject, ignore_unknown=False, remove_self=False):
  425. try:
  426. if isinstance(pyobject, pyobjects.AbstractClass):
  427. pyobject = pyobject['__init__'].get_object()
  428. if not isinstance(pyobject, pyobjects.AbstractFunction):
  429. pyobject = pyobject['__call__'].get_object()
  430. except exceptions.AttributeNotFoundError:
  431. return None
  432. if ignore_unknown and not isinstance(pyobject, pyobjects.PyFunction):
  433. return
  434. if isinstance(pyobject, pyobjects.AbstractFunction):
  435. result = self._get_function_signature(pyobject, add_module=True)
  436. if remove_self and self._is_method(pyobject):
  437. return result.replace('(self)', '()').replace('(self, ', '(')
  438. return result
  439. def _get_class_docstring(self, pyclass):
  440. contents = self._trim_docstring(pyclass.get_doc(), 2)
  441. supers = [super.get_name() for super in pyclass.get_superclasses()]
  442. doc = 'class %s(%s):\n\n' % (pyclass.get_name(), ', '.join(supers)) + contents
  443. if '__init__' in pyclass:
  444. init = pyclass['__init__'].get_object()
  445. if isinstance(init, pyobjects.AbstractFunction):
  446. doc += '\n\n' + self._get_single_function_docstring(init)
  447. return doc
  448. def _get_function_docstring(self, pyfunction):
  449. functions = [pyfunction]
  450. if self._is_method(pyfunction):
  451. functions.extend(self._get_super_methods(pyfunction.parent,
  452. pyfunction.get_name()))
  453. return '\n\n'.join([self._get_single_function_docstring(function)
  454. for function in functions])
  455. def _is_method(self, pyfunction):
  456. return isinstance(pyfunction, pyobjects.PyFunction) and \
  457. isinstance(pyfunction.parent, pyobjects.PyClass)
  458. def _get_single_function_docstring(self, pyfunction):
  459. signature = self._get_function_signature(pyfunction)
  460. docs = self._trim_docstring(pyfunction.get_doc(), indents=2)
  461. return signature + ':\n\n' + docs
  462. def _get_super_methods(self, pyclass, name):
  463. result = []
  464. for super_class in pyclass.get_superclasses():
  465. if name in super_class:
  466. function = super_class[name].get_object()
  467. if isinstance(function, pyobjects.AbstractFunction):
  468. result.append(function)
  469. result.extend(self._get_super_methods(super_class, name))
  470. return result
  471. def _get_function_signature(self, pyfunction, add_module=False):
  472. location = self._location(pyfunction, add_module)
  473. if isinstance(pyfunction, pyobjects.PyFunction):
  474. info = functionutils.DefinitionInfo.read(pyfunction)
  475. return location + info.to_string()
  476. else:
  477. return '%s(%s)' % (location + pyfunction.get_name(),
  478. ', '.join(pyfunction.get_param_names()))
  479. def _location(self, pyobject, add_module=False):
  480. location = []
  481. parent = pyobject.parent
  482. while parent and not isinstance(parent, pyobjects.AbstractModule):
  483. location.append(parent.get_name())
  484. location.append('.')
  485. parent = parent.parent
  486. if add_module:
  487. if isinstance(pyobject, pyobjects.PyFunction):
  488. module = pyobject.get_module()
  489. location.insert(0, self._get_module(pyobject))
  490. if isinstance(parent, builtins.BuiltinModule):
  491. location.insert(0, parent.get_name() + '.')
  492. return ''.join(location)
  493. def _get_module(self, pyfunction):
  494. module = pyfunction.get_module()
  495. if module is not None:
  496. resource = module.get_resource()
  497. if resource is not None:
  498. return pyfunction.pycore.modname(resource) + '.'
  499. return ''
  500. def _trim_docstring(self, docstring, indents=0):
  501. """The sample code from :PEP:`257`"""
  502. if not docstring:
  503. return ''
  504. # Convert tabs to spaces (following normal Python rules)
  505. # and split into a list of lines:
  506. lines = docstring.expandtabs().splitlines()
  507. # Determine minimum indentation (first line doesn't count):
  508. indent = sys.maxint
  509. for line in lines[1:]:
  510. stripped = line.lstrip()
  511. if stripped:
  512. indent = min(indent, len(line) - len(stripped))
  513. # Remove indentation (first line is special):
  514. trimmed = [lines[0].strip()]
  515. if indent < sys.maxint:
  516. for line in lines[1:]:
  517. trimmed.append(line[indent:].rstrip())
  518. # Strip off trailing and leading blank lines:
  519. while trimmed and not trimmed[-1]:
  520. trimmed.pop()
  521. while trimmed and not trimmed[0]:
  522. trimmed.pop(0)
  523. # Return a single string:
  524. return '\n'.join((' ' * indents + line for line in trimmed))
  525. # Deprecated classes
  526. class TemplateProposal(CodeAssistProposal):
  527. def __init__(self, name, template):
  528. warnings.warn('TemplateProposal is deprecated.',
  529. DeprecationWarning, stacklevel=2)
  530. super(TemplateProposal, self).__init__(name, 'template')
  531. self.template = template
  532. class Template(object):
  533. def __init__(self, template):
  534. self.template = template
  535. warnings.warn('Template is deprecated.',
  536. DeprecationWarning, stacklevel=2)
  537. def variables(self):
  538. return []
  539. def substitute(self, mapping):
  540. return self.template
  541. def get_cursor_location(self, mapping):
  542. return len(self.template)