Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(604)

Unified Diff: tools/telemetry/third_party/rope/rope/refactor/patchedast.py

Issue 1132103009: Example of refactoring using rope library. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/third_party/rope/rope/refactor/patchedast.py
diff --git a/tools/telemetry/third_party/rope/rope/refactor/patchedast.py b/tools/telemetry/third_party/rope/rope/refactor/patchedast.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2b8db27f3f392f753001c631f298375204ed415
--- /dev/null
+++ b/tools/telemetry/third_party/rope/rope/refactor/patchedast.py
@@ -0,0 +1,753 @@
+import collections
+import re
+import warnings
+
+from rope.base import ast, codeanalyze, exceptions
+
+try:
+ basestring
+except NameError:
+ basestring = (str, bytes)
+
+def get_patched_ast(source, sorted_children=False):
+ """Adds ``region`` and ``sorted_children`` fields to nodes
+
+ Adds ``sorted_children`` field only if `sorted_children` is True.
+
+ """
+ return patch_ast(ast.parse(source), source, sorted_children)
+
+
+def patch_ast(node, source, sorted_children=False):
+ """Patches the given node
+
+ After calling, each node in `node` will have a new field named
+ `region` that is a tuple containing the start and end offsets
+ of the code that generated it.
+
+ If `sorted_children` is true, a `sorted_children` field will
+ be created for each node, too. It is a list containing child
+ nodes as well as whitespaces and comments that occur between
+ them.
+
+ """
+ if hasattr(node, 'region'):
+ return node
+ walker = _PatchingASTWalker(source, children=sorted_children)
+ ast.call_for_nodes(node, walker)
+ return node
+
+
+def node_region(patched_ast_node):
+ """Get the region of a patched ast node"""
+ return patched_ast_node.region
+
+
+def write_ast(patched_ast_node):
+ """Extract source form a patched AST node with `sorted_children` field
+
+ If the node is patched with sorted_children turned off you can use
+ `node_region` function for obtaining code using module source code.
+ """
+ result = []
+ for child in patched_ast_node.sorted_children:
+ if isinstance(child, ast.AST):
+ result.append(write_ast(child))
+ else:
+ result.append(child)
+ return ''.join(result)
+
+
+class MismatchedTokenError(exceptions.RopeError):
+ pass
+
+
+class _PatchingASTWalker(object):
+
+ def __init__(self, source, children=False):
+ self.source = _Source(source)
+ self.children = children
+ self.lines = codeanalyze.SourceLinesAdapter(source)
+ self.children_stack = []
+
+ Number = object()
+ String = object()
+ semicolon_or_as_in_except = object()
+
+ def __call__(self, node):
+ method = getattr(self, '_' + node.__class__.__name__, None)
+ if method is not None:
+ return method(node)
+ # ???: Unknown node; what should we do here?
+ warnings.warn('Unknown node type <%s>; please report!'
+ % node.__class__.__name__, RuntimeWarning)
+ node.region = (self.source.offset, self.source.offset)
+ if self.children:
+ node.sorted_children = ast.get_children(node)
+
+ def _handle(self, node, base_children, eat_parens=False, eat_spaces=False):
+ if hasattr(node, 'region'):
+ # ???: The same node was seen twice; what should we do?
+ warnings.warn(
+ 'Node <%s> has been already patched; please report!' %
+ node.__class__.__name__, RuntimeWarning)
+ return
+ base_children = collections.deque(base_children)
+ self.children_stack.append(base_children)
+ children = collections.deque()
+ formats = []
+ suspected_start = self.source.offset
+ start = suspected_start
+ first_token = True
+ while base_children:
+ child = base_children.popleft()
+ if child is None:
+ continue
+ offset = self.source.offset
+ if isinstance(child, ast.AST):
+ ast.call_for_nodes(child, self)
+ token_start = child.region[0]
+ else:
+ if child is self.String:
+ region = self.source.consume_string(
+ end=self._find_next_statement_start())
+ elif child is self.Number:
+ region = self.source.consume_number()
+ elif child == '!=':
+ # INFO: This has been added to handle deprecated ``<>``
+ region = self.source.consume_not_equal()
+ elif child == self.semicolon_or_as_in_except:
+ # INFO: This has been added to handle deprecated
+ # semicolon in except
+ region = self.source.consume_except_as_or_semicolon()
+ else:
+ region = self.source.consume(child)
+ child = self.source[region[0]:region[1]]
+ token_start = region[0]
+ if not first_token:
+ formats.append(self.source[offset:token_start])
+ if self.children:
+ children.append(self.source[offset:token_start])
+ else:
+ first_token = False
+ start = token_start
+ if self.children:
+ children.append(child)
+ start = self._handle_parens(children, start, formats)
+ if eat_parens:
+ start = self._eat_surrounding_parens(
+ children, suspected_start, start)
+ if eat_spaces:
+ if self.children:
+ children.appendleft(self.source[0:start])
+ end_spaces = self.source[self.source.offset:]
+ self.source.consume(end_spaces)
+ if self.children:
+ children.append(end_spaces)
+ start = 0
+ if self.children:
+ node.sorted_children = children
+ node.region = (start, self.source.offset)
+ self.children_stack.pop()
+
+ def _handle_parens(self, children, start, formats):
+ """Changes `children` and returns new start"""
+ opens, closes = self._count_needed_parens(formats)
+ old_end = self.source.offset
+ new_end = None
+ for i in range(closes):
+ new_end = self.source.consume(')')[1]
+ if new_end is not None:
+ if self.children:
+ children.append(self.source[old_end:new_end])
+ new_start = start
+ for i in range(opens):
+ new_start = self.source.rfind_token('(', 0, new_start)
+ if new_start != start:
+ if self.children:
+ children.appendleft(self.source[new_start:start])
+ start = new_start
+ return start
+
+ def _eat_surrounding_parens(self, children, suspected_start, start):
+ index = self.source.rfind_token('(', suspected_start, start)
+ if index is not None:
+ old_start = start
+ old_offset = self.source.offset
+ start = index
+ if self.children:
+ children.appendleft(self.source[start + 1:old_start])
+ children.appendleft('(')
+ token_start, token_end = self.source.consume(')')
+ if self.children:
+ children.append(self.source[old_offset:token_start])
+ children.append(')')
+ return start
+
+ def _count_needed_parens(self, children):
+ start = 0
+ opens = 0
+ for child in children:
+ if not isinstance(child, basestring):
+ continue
+ if child == '' or child[0] in '\'"':
+ continue
+ index = 0
+ while index < len(child):
+ if child[index] == ')':
+ if opens > 0:
+ opens -= 1
+ else:
+ start += 1
+ if child[index] == '(':
+ opens += 1
+ if child[index] == '#':
+ try:
+ index = child.index('\n', index)
+ except ValueError:
+ break
+ index += 1
+ return start, opens
+
+ def _find_next_statement_start(self):
+ for children in reversed(self.children_stack):
+ for child in children:
+ if isinstance(child, ast.stmt):
+ return child.col_offset \
+ + self.lines.get_line_start(child.lineno)
+ return len(self.source.source)
+
+ _operators = {'And': 'and', 'Or': 'or', 'Add': '+', 'Sub': '-',
+ 'Mult': '*', 'Div': '/', 'Mod': '%', 'Pow': '**',
+ 'LShift': '<<', 'RShift': '>>', 'BitOr': '|', 'BitAnd': '&',
+ 'BitXor': '^', 'FloorDiv': '//', 'Invert': '~',
+ 'Not': 'not', 'UAdd': '+', 'USub': '-', 'Eq': '==',
+ 'NotEq': '!=', 'Lt': '<', 'LtE': '<=', 'Gt': '>',
+ 'GtE': '>=', 'Is': 'is', 'IsNot': 'is not', 'In': 'in',
+ 'NotIn': 'not in'}
+
+ def _get_op(self, node):
+ return self._operators[node.__class__.__name__].split(' ')
+
+ def _Attribute(self, node):
+ self._handle(node, [node.value, '.', node.attr])
+
+ def _Assert(self, node):
+ children = ['assert', node.test]
+ if node.msg:
+ children.append(',')
+ children.append(node.msg)
+ self._handle(node, children)
+
+ def _Assign(self, node):
+ children = self._child_nodes(node.targets, '=')
+ children.append('=')
+ children.append(node.value)
+ self._handle(node, children)
+
+ def _AugAssign(self, node):
+ children = [node.target]
+ children.extend(self._get_op(node.op))
+ children.extend(['=', node.value])
+ self._handle(node, children)
+
+ def _Repr(self, node):
+ self._handle(node, ['`', node.value, '`'])
+
+ def _BinOp(self, node):
+ children = [node.left] + self._get_op(node.op) + [node.right]
+ self._handle(node, children)
+
+ def _BoolOp(self, node):
+ self._handle(node, self._child_nodes(node.values,
+ self._get_op(node.op)[0]))
+
+ def _Break(self, node):
+ self._handle(node, ['break'])
+
+ def _Call(self, node):
+ children = [node.func, '(']
+ args = list(node.args) + node.keywords
+ children.extend(self._child_nodes(args, ','))
+ if node.starargs is not None:
+ if args:
+ children.append(',')
+ children.extend(['*', node.starargs])
+ if node.kwargs is not None:
+ if args or node.starargs is not None:
+ children.append(',')
+ children.extend(['**', node.kwargs])
+ children.append(')')
+ self._handle(node, children)
+
+ def _ClassDef(self, node):
+ children = []
+ if getattr(node, 'decorator_list', None):
+ for decorator in node.decorator_list:
+ children.append('@')
+ children.append(decorator)
+ children.extend(['class', node.name])
+ if node.bases:
+ children.append('(')
+ children.extend(self._child_nodes(node.bases, ','))
+ children.append(')')
+ children.append(':')
+ children.extend(node.body)
+ self._handle(node, children)
+
+ def _Compare(self, node):
+ children = []
+ children.append(node.left)
+ for op, expr in zip(node.ops, node.comparators):
+ children.extend(self._get_op(op))
+ children.append(expr)
+ self._handle(node, children)
+
+ def _Delete(self, node):
+ self._handle(node, ['del'] + self._child_nodes(node.targets, ','))
+
+ def _Num(self, node):
+ self._handle(node, [self.Number])
+
+ def _Str(self, node):
+ self._handle(node, [self.String])
+
+ def _Continue(self, node):
+ self._handle(node, ['continue'])
+
+ def _Dict(self, node):
+ children = []
+ children.append('{')
+ if node.keys:
+ for index, (key, value) in enumerate(zip(node.keys, node.values)):
+ children.extend([key, ':', value])
+ if index < len(node.keys) - 1:
+ children.append(',')
+ children.append('}')
+ self._handle(node, children)
+
+ def _Ellipsis(self, node):
+ self._handle(node, ['...'])
+
+ def _Expr(self, node):
+ self._handle(node, [node.value])
+
+ def _Exec(self, node):
+ children = []
+ children.extend(['exec', node.body])
+ if node.globals:
+ children.extend(['in', node.globals])
+ if node.locals:
+ children.extend([',', node.locals])
+ self._handle(node, children)
+
+ def _ExtSlice(self, node):
+ children = []
+ for index, dim in enumerate(node.dims):
+ if index > 0:
+ children.append(',')
+ children.append(dim)
+ self._handle(node, children)
+
+ def _For(self, node):
+ children = ['for', node.target, 'in', node.iter, ':']
+ children.extend(node.body)
+ if node.orelse:
+ children.extend(['else', ':'])
+ children.extend(node.orelse)
+ self._handle(node, children)
+
+ def _ImportFrom(self, node):
+ children = ['from']
+ if node.level:
+ children.append('.' * node.level)
+ # see comment at rope.base.ast.walk
+ children.extend([node.module or '',
+ 'import'])
+ children.extend(self._child_nodes(node.names, ','))
+ self._handle(node, children)
+
+ def _alias(self, node):
+ children = [node.name]
+ if node.asname:
+ children.extend(['as', node.asname])
+ self._handle(node, children)
+
+ def _FunctionDef(self, node):
+ children = []
+ try:
+ decorators = getattr(node, 'decorator_list')
+ except AttributeError:
+ decorators = getattr(node, 'decorators', None)
+ if decorators:
+ for decorator in decorators:
+ children.append('@')
+ children.append(decorator)
+ children.extend(['def', node.name, '(', node.args])
+ children.extend([')', ':'])
+ children.extend(node.body)
+ self._handle(node, children)
+
+ def _arguments(self, node):
+ children = []
+ args = list(node.args)
+ defaults = [None] * (len(args) - len(node.defaults)) + \
+ list(node.defaults)
+ for index, (arg, default) in enumerate(zip(args, defaults)):
+ if index > 0:
+ children.append(',')
+ self._add_args_to_children(children, arg, default)
+ if node.vararg is not None:
+ if args:
+ children.append(',')
+ children.extend(['*', node.vararg])
+ if node.kwarg is not None:
+ if args or node.vararg is not None:
+ children.append(',')
+ children.extend(['**', node.kwarg])
+ self._handle(node, children)
+
+ def _add_args_to_children(self, children, arg, default):
+ if isinstance(arg, (list, tuple)):
+ self._add_tuple_parameter(children, arg)
+ else:
+ children.append(arg)
+ if default is not None:
+ children.append('=')
+ children.append(default)
+
+ def _add_tuple_parameter(self, children, arg):
+ children.append('(')
+ for index, token in enumerate(arg):
+ if index > 0:
+ children.append(',')
+ if isinstance(token, (list, tuple)):
+ self._add_tuple_parameter(children, token)
+ else:
+ children.append(token)
+ children.append(')')
+
+ def _GeneratorExp(self, node):
+ children = [node.elt]
+ children.extend(node.generators)
+ self._handle(node, children, eat_parens=True)
+
+ def _comprehension(self, node):
+ children = ['for', node.target, 'in', node.iter]
+ if node.ifs:
+ for if_ in node.ifs:
+ children.append('if')
+ children.append(if_)
+ self._handle(node, children)
+
+ def _Global(self, node):
+ children = self._child_nodes(node.names, ',')
+ children.insert(0, 'global')
+ self._handle(node, children)
+
+ def _If(self, node):
+ if self._is_elif(node):
+ children = ['elif']
+ else:
+ children = ['if']
+ children.extend([node.test, ':'])
+ children.extend(node.body)
+ if node.orelse:
+ if len(node.orelse) == 1 and self._is_elif(node.orelse[0]):
+ pass
+ else:
+ children.extend(['else', ':'])
+ children.extend(node.orelse)
+ self._handle(node, children)
+
+ def _is_elif(self, node):
+ if not isinstance(node, ast.If):
+ return False
+ offset = self.lines.get_line_start(node.lineno) + node.col_offset
+ word = self.source[offset:offset + 4]
+ # XXX: This is a bug; the offset does not point to the first
+ alt_word = self.source[offset - 5:offset - 1]
+ return 'elif' in (word, alt_word)
+
+ def _IfExp(self, node):
+ return self._handle(node, [node.body, 'if', node.test,
+ 'else', node.orelse])
+
+ def _Import(self, node):
+ children = ['import']
+ children.extend(self._child_nodes(node.names, ','))
+ self._handle(node, children)
+
+ def _keyword(self, node):
+ self._handle(node, [node.arg, '=', node.value])
+
+ def _Lambda(self, node):
+ self._handle(node, ['lambda', node.args, ':', node.body])
+
+ def _List(self, node):
+ self._handle(node, ['['] + self._child_nodes(node.elts, ',') + [']'])
+
+ def _ListComp(self, node):
+ children = ['[', node.elt]
+ children.extend(node.generators)
+ children.append(']')
+ self._handle(node, children)
+
+ def _Module(self, node):
+ self._handle(node, list(node.body), eat_spaces=True)
+
+ def _Name(self, node):
+ self._handle(node, [node.id])
+
+ def _Pass(self, node):
+ self._handle(node, ['pass'])
+
+ def _Print(self, node):
+ children = ['print']
+ if node.dest:
+ children.extend(['>>', node.dest])
+ if node.values:
+ children.append(',')
+ children.extend(self._child_nodes(node.values, ','))
+ if not node.nl:
+ children.append(',')
+ self._handle(node, children)
+
+ def _Raise(self, node):
+ children = ['raise']
+ if node.type:
+ children.append(node.type)
+ if node.inst:
+ children.append(',')
+ children.append(node.inst)
+ if node.tback:
+ children.append(',')
+ children.append(node.tback)
+ self._handle(node, children)
+
+ def _Return(self, node):
+ children = ['return']
+ if node.value:
+ children.append(node.value)
+ self._handle(node, children)
+
+ def _Sliceobj(self, node):
+ children = []
+ for index, slice in enumerate(node.nodes):
+ if index > 0:
+ children.append(':')
+ if slice:
+ children.append(slice)
+ self._handle(node, children)
+
+ def _Index(self, node):
+ self._handle(node, [node.value])
+
+ def _Subscript(self, node):
+ self._handle(node, [node.value, '[', node.slice, ']'])
+
+ def _Slice(self, node):
+ children = []
+ if node.lower:
+ children.append(node.lower)
+ children.append(':')
+ if node.upper:
+ children.append(node.upper)
+ if node.step:
+ children.append(':')
+ children.append(node.step)
+ self._handle(node, children)
+
+ def _TryFinally(self, node):
+ children = []
+ if len(node.body) != 1 or not isinstance(node.body[0], ast.TryExcept):
+ children.extend(['try', ':'])
+ children.extend(node.body)
+ children.extend(['finally', ':'])
+ children.extend(node.finalbody)
+ self._handle(node, children)
+
+ def _TryExcept(self, node):
+ children = ['try', ':']
+ children.extend(node.body)
+ children.extend(node.handlers)
+ if node.orelse:
+ children.extend(['else', ':'])
+ children.extend(node.orelse)
+ self._handle(node, children)
+
+ def _ExceptHandler(self, node):
+ self._excepthandler(node)
+
+ def _excepthandler(self, node):
+ # self._handle(node, [self.semicolon_or_as_in_except])
+ children = ['except']
+ if node.type:
+ children.append(node.type)
+ if node.name:
+ children.append(self.semicolon_or_as_in_except)
+ children.append(node.name)
+ children.append(':')
+ children.extend(node.body)
+
+ self._handle(node, children)
+
+ def _Tuple(self, node):
+ if node.elts:
+ self._handle(node, self._child_nodes(node.elts, ','),
+ eat_parens=True)
+ else:
+ self._handle(node, ['(', ')'])
+
+ def _UnaryOp(self, node):
+ children = self._get_op(node.op)
+ children.append(node.operand)
+ self._handle(node, children)
+
+ def _Yield(self, node):
+ children = ['yield']
+ if node.value:
+ children.append(node.value)
+ self._handle(node, children)
+
+ def _While(self, node):
+ children = ['while', node.test, ':']
+ children.extend(node.body)
+ if node.orelse:
+ children.extend(['else', ':'])
+ children.extend(node.orelse)
+ self._handle(node, children)
+
+ def _With(self, node):
+ children = ['with', node.context_expr]
+ if node.optional_vars:
+ children.extend(['as', node.optional_vars])
+ children.append(':')
+ children.extend(node.body)
+ self._handle(node, children)
+
+ def _child_nodes(self, nodes, separator):
+ children = []
+ for index, child in enumerate(nodes):
+ children.append(child)
+ if index < len(nodes) - 1:
+ children.append(separator)
+ return children
+
+
+class _Source(object):
+
+ def __init__(self, source):
+ self.source = source
+ self.offset = 0
+
+ def consume(self, token):
+ try:
+ while True:
+ new_offset = self.source.index(token, self.offset)
+ if self._good_token(token, new_offset):
+ break
+ else:
+ self._skip_comment()
+ except (ValueError, TypeError):
+ raise MismatchedTokenError(
+ 'Token <%s> at %s cannot be matched' %
+ (token, self._get_location()))
+ self.offset = new_offset + len(token)
+ return (new_offset, self.offset)
+
+ def consume_string(self, end=None):
+ if _Source._string_pattern is None:
+ original = codeanalyze.get_string_pattern()
+ pattern = r'(%s)((\s|\\\n|#[^\n]*\n)*(%s))*' % \
+ (original, original)
+ _Source._string_pattern = re.compile(pattern)
+ repattern = _Source._string_pattern
+ return self._consume_pattern(repattern, end)
+
+ def consume_number(self):
+ if _Source._number_pattern is None:
+ _Source._number_pattern = re.compile(
+ self._get_number_pattern())
+ repattern = _Source._number_pattern
+ return self._consume_pattern(repattern)
+
+ def consume_not_equal(self):
+ if _Source._not_equals_pattern is None:
+ _Source._not_equals_pattern = re.compile(r'<>|!=')
+ repattern = _Source._not_equals_pattern
+ return self._consume_pattern(repattern)
+
+ def consume_except_as_or_semicolon(self):
+ repattern = re.compile(r'as|,')
+ return self._consume_pattern(repattern)
+
+ def _good_token(self, token, offset, start=None):
+ """Checks whether consumed token is in comments"""
+ if start is None:
+ start = self.offset
+ try:
+ comment_index = self.source.rindex('#', start, offset)
+ except ValueError:
+ return True
+ try:
+ new_line_index = self.source.rindex('\n', start, offset)
+ except ValueError:
+ return False
+ return comment_index < new_line_index
+
+ def _skip_comment(self):
+ self.offset = self.source.index('\n', self.offset + 1)
+
+ def _get_location(self):
+ lines = self.source[:self.offset].split('\n')
+ return (len(lines), len(lines[-1]))
+
+ def _consume_pattern(self, repattern, end=None):
+ while True:
+ if end is None:
+ end = len(self.source)
+ match = repattern.search(self.source, self.offset, end)
+ if self._good_token(match.group(), match.start()):
+ break
+ else:
+ self._skip_comment()
+ self.offset = match.end()
+ return match.start(), match.end()
+
+ def till_token(self, token):
+ new_offset = self.source.index(token, self.offset)
+ return self[self.offset:new_offset]
+
+ def rfind_token(self, token, start, end):
+ index = start
+ while True:
+ try:
+ index = self.source.rindex(token, start, end)
+ if self._good_token(token, index, start=start):
+ return index
+ else:
+ end = index
+ except ValueError:
+ return None
+
+ def from_offset(self, offset):
+ return self[offset:self.offset]
+
+ def find_backwards(self, pattern, offset):
+ return self.source.rindex(pattern, 0, offset)
+
+ def __getitem__(self, index):
+ return self.source[index]
+
+ def __getslice__(self, i, j):
+ return self.source[i:j]
+
+ def _get_number_pattern(self):
+ # HACK: It is merely an approaximation and does the job
+ integer = r'(0|0x)?[\da-fA-F]+[lL]?'
+ return r'(%s(\.\d*)?|(\.\d+))([eE][-+]?\d*)?[jJ]?' % integer
+
+ _string_pattern = None
+ _number_pattern = None
+ _not_equals_pattern = None

Powered by Google App Engine
This is Rietveld 408576698