Path: blob/master/elisp/emacs-for-python/rope-dist/rope/contrib/fixsyntax.py
1415 views
import rope.base.codeanalyze1import rope.base.evaluate2from rope.base import worder, exceptions, utils3from rope.base.codeanalyze import ArrayLinesAdapter, LogicalLineFinder456class FixSyntax(object):78def __init__(self, pycore, code, resource, maxfixes=1):9self.pycore = pycore10self.code = code11self.resource = resource12self.maxfixes = maxfixes1314@utils.saveit15def get_pymodule(self):16"""Get a `PyModule`"""17errors = []18code = self.code19tries = 020while True:21try:22if tries == 0 and self.resource is not None and \23self.resource.read() == code:24return self.pycore.resource_to_pyobject(self.resource,25force_errors=True)26return self.pycore.get_string_module(27code, resource=self.resource, force_errors=True)28except exceptions.ModuleSyntaxError, e:29if tries < self.maxfixes:30tries += 131self.commenter.comment(e.lineno)32code = '\n'.join(self.commenter.lines)33errors.append(' * line %s: %s ... fixed' % (e.lineno,34e.message_))35else:36errors.append(' * line %s: %s ... raised!' % (e.lineno,37e.message_))38new_message = ('\nSyntax errors in file %s:\n' % e.filename) \39+ '\n'.join(errors)40raise exceptions.ModuleSyntaxError(e.filename, e.lineno,41new_message)4243@property44@utils.saveit45def commenter(self):46return _Commenter(self.code)4748def pyname_at(self, offset):49pymodule = self.get_pymodule()50def old_pyname():51word_finder = worder.Worder(self.code, True)52expression = word_finder.get_primary_at(offset)53expression = expression.replace('\\\n', ' ').replace('\n', ' ')54lineno = self.code.count('\n', 0, offset)55scope = pymodule.get_scope().get_inner_scope_for_line(lineno)56return rope.base.evaluate.eval_str(scope, expression)57new_code = pymodule.source_code58def new_pyname():59newoffset = self.commenter.transfered_offset(offset)60return rope.base.evaluate.eval_location(pymodule, newoffset)61if new_code.startswith(self.code[:offset + 1]):62return new_pyname()63result = old_pyname()64if result is None:65return new_pyname()66return result676869class _Commenter(object):7071def __init__(self, code):72self.code = code73self.lines = self.code.split('\n')74self.lines.append('\n')75self.origs = range(len(self.lines) + 1)76self.diffs = [0] * (len(self.lines) + 1)7778def comment(self, lineno):79start = _logical_start(self.lines, lineno, check_prev=True) - 180# using self._get_stmt_end() instead of self._get_block_end()81# to lower commented lines82end = self._get_stmt_end(start)83indents = _get_line_indents(self.lines[start])84if 0 < start:85last_lineno = self._last_non_blank(start - 1)86last_line = self.lines[last_lineno]87if last_line.rstrip().endswith(':'):88indents = _get_line_indents(last_line) + 489self._set(start, ' ' * indents + 'pass')90for line in range(start + 1, end + 1):91self._set(line, self.lines[start])92self._fix_incomplete_try_blocks(lineno, indents)9394def transfered_offset(self, offset):95lineno = self.code.count('\n', 0, offset)96diff = sum(self.diffs[:lineno])97return offset + diff9899def _last_non_blank(self, start):100while start > 0 and self.lines[start].strip() == '':101start -= 1102return start103104def _get_block_end(self, lineno):105end_line = lineno106base_indents = _get_line_indents(self.lines[lineno])107for i in range(lineno + 1, len(self.lines)):108if _get_line_indents(self.lines[i]) >= base_indents:109end_line = i110else:111break112return end_line113114def _get_stmt_end(self, lineno):115end_line = lineno116base_indents = _get_line_indents(self.lines[lineno])117for i in range(lineno + 1, len(self.lines)):118if _get_line_indents(self.lines[i]) <= base_indents:119return i - 1120return lineno121122def _fix_incomplete_try_blocks(self, lineno, indents):123block_start = lineno124last_indents = current_indents = indents125while block_start > 0:126block_start = rope.base.codeanalyze.get_block_start(127ArrayLinesAdapter(self.lines), block_start) - 1128if self.lines[block_start].strip().startswith('try:'):129indents = _get_line_indents(self.lines[block_start])130if indents > last_indents:131continue132last_indents = indents133block_end = self._find_matching_deindent(block_start)134line = self.lines[block_end].strip()135if not (line.startswith('finally:') or136line.startswith('except ') or137line.startswith('except:')):138self._insert(block_end, ' ' * indents + 'finally:')139self._insert(block_end + 1, ' ' * indents + ' pass')140141def _find_matching_deindent(self, line_number):142indents = _get_line_indents(self.lines[line_number])143current_line = line_number + 1144while current_line < len(self.lines):145line = self.lines[current_line]146if not line.strip().startswith('#') and not line.strip() == '':147# HACK: We should have used logical lines here148if _get_line_indents(self.lines[current_line]) <= indents:149return current_line150current_line += 1151return len(self.lines) - 1152153def _set(self, lineno, line):154self.diffs[self.origs[lineno]] += len(line) - len(self.lines[lineno])155self.lines[lineno] = line156157def _insert(self, lineno, line):158self.diffs[self.origs[lineno]] += len(line) + 1159self.origs.insert(lineno, self.origs[lineno])160self.lines.insert(lineno, line)161162def _logical_start(lines, lineno, check_prev=False):163logical_finder = LogicalLineFinder(ArrayLinesAdapter(lines))164if check_prev:165prev = lineno - 1166while prev > 0:167start, end = logical_finder.logical_line_in(prev)168if end is None or start <= lineno < end:169return start170if start <= prev:171break172prev -= 1173return logical_finder.logical_line_in(lineno)[0]174175176def _get_line_indents(line):177return rope.base.codeanalyze.count_line_indents(line)178179180