Index: third_party/google-endpoints/libfuturize/fixer_util.py |
diff --git a/third_party/google-endpoints/libfuturize/fixer_util.py b/third_party/google-endpoints/libfuturize/fixer_util.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ce1e9753b6cf0fc7005c67032d9c56a088c2012c |
--- /dev/null |
+++ b/third_party/google-endpoints/libfuturize/fixer_util.py |
@@ -0,0 +1,518 @@ |
+""" |
+Utility functions from 2to3, 3to2 and python-modernize (and some home-grown |
+ones). |
+ |
+Licences: |
+2to3: PSF License v2 |
+3to2: Apache Software License (from 3to2/setup.py) |
+python-modernize licence: BSD (from python-modernize/LICENSE) |
+""" |
+ |
+from lib2to3.fixer_util import (FromImport, Newline, is_import, |
+ find_root, does_tree_import, Comma) |
+from lib2to3.pytree import Leaf, Node |
+from lib2to3.pygram import python_symbols as syms, python_grammar |
+from lib2to3.pygram import token |
+from lib2to3.fixer_util import (Node, Call, Name, syms, Comma, Number) |
+import re |
+ |
+ |
+def canonical_fix_name(fix, avail_fixes): |
+ """ |
+ Examples: |
+ >>> canonical_fix_name('fix_wrap_text_literals') |
+ 'libfuturize.fixes.fix_wrap_text_literals' |
+ >>> canonical_fix_name('wrap_text_literals') |
+ 'libfuturize.fixes.fix_wrap_text_literals' |
+ >>> canonical_fix_name('wrap_te') |
+ ValueError("unknown fixer name") |
+ >>> canonical_fix_name('wrap') |
+ ValueError("ambiguous fixer name") |
+ """ |
+ if ".fix_" in fix: |
+ return fix |
+ else: |
+ if fix.startswith('fix_'): |
+ fix = fix[4:] |
+ # Infer the full module name for the fixer. |
+ # First ensure that no names clash (e.g. |
+ # lib2to3.fixes.fix_blah and libfuturize.fixes.fix_blah): |
+ found = [f for f in avail_fixes |
+ if f.endswith('fix_{0}'.format(fix))] |
+ if len(found) > 1: |
+ raise ValueError("Ambiguous fixer name. Choose a fully qualified " |
+ "module name instead from these:\n" + |
+ "\n".join(" " + myf for myf in found)) |
+ elif len(found) == 0: |
+ raise ValueError("Unknown fixer. Use --list-fixes or -l for a list.") |
+ return found[0] |
+ |
+ |
+ |
+## These functions are from 3to2 by Joe Amenta: |
+ |
+def Star(prefix=None): |
+ return Leaf(token.STAR, u'*', prefix=prefix) |
+ |
+def DoubleStar(prefix=None): |
+ return Leaf(token.DOUBLESTAR, u'**', prefix=prefix) |
+ |
+def Minus(prefix=None): |
+ return Leaf(token.MINUS, u'-', prefix=prefix) |
+ |
+def commatize(leafs): |
+ """ |
+ Accepts/turns: (Name, Name, ..., Name, Name) |
+ Returns/into: (Name, Comma, Name, Comma, ..., Name, Comma, Name) |
+ """ |
+ new_leafs = [] |
+ for leaf in leafs: |
+ new_leafs.append(leaf) |
+ new_leafs.append(Comma()) |
+ del new_leafs[-1] |
+ return new_leafs |
+ |
+def indentation(node): |
+ """ |
+ Returns the indentation for this node |
+ Iff a node is in a suite, then it has indentation. |
+ """ |
+ while node.parent is not None and node.parent.type != syms.suite: |
+ node = node.parent |
+ if node.parent is None: |
+ return u"" |
+ # The first three children of a suite are NEWLINE, INDENT, (some other node) |
+ # INDENT.value contains the indentation for this suite |
+ # anything after (some other node) has the indentation as its prefix. |
+ if node.type == token.INDENT: |
+ return node.value |
+ elif node.prev_sibling is not None and node.prev_sibling.type == token.INDENT: |
+ return node.prev_sibling.value |
+ elif node.prev_sibling is None: |
+ return u"" |
+ else: |
+ return node.prefix |
+ |
+def indentation_step(node): |
+ """ |
+ Dirty little trick to get the difference between each indentation level |
+ Implemented by finding the shortest indentation string |
+ (technically, the "least" of all of the indentation strings, but |
+ tabs and spaces mixed won't get this far, so those are synonymous.) |
+ """ |
+ r = find_root(node) |
+ # Collect all indentations into one set. |
+ all_indents = set(i.value for i in r.pre_order() if i.type == token.INDENT) |
+ if not all_indents: |
+ # nothing is indented anywhere, so we get to pick what we want |
+ return u" " # four spaces is a popular convention |
+ else: |
+ return min(all_indents) |
+ |
+def suitify(parent): |
+ """ |
+ Turn the stuff after the first colon in parent's children |
+ into a suite, if it wasn't already |
+ """ |
+ for node in parent.children: |
+ if node.type == syms.suite: |
+ # already in the prefered format, do nothing |
+ return |
+ |
+ # One-liners have no suite node, we have to fake one up |
+ for i, node in enumerate(parent.children): |
+ if node.type == token.COLON: |
+ break |
+ else: |
+ raise ValueError(u"No class suite and no ':'!") |
+ # Move everything into a suite node |
+ suite = Node(syms.suite, [Newline(), Leaf(token.INDENT, indentation(node) + indentation_step(node))]) |
+ one_node = parent.children[i+1] |
+ one_node.remove() |
+ one_node.prefix = u'' |
+ suite.append_child(one_node) |
+ parent.append_child(suite) |
+ |
+def NameImport(package, as_name=None, prefix=None): |
+ """ |
+ Accepts a package (Name node), name to import it as (string), and |
+ optional prefix and returns a node: |
+ import <package> [as <as_name>] |
+ """ |
+ if prefix is None: |
+ prefix = u"" |
+ children = [Name(u"import", prefix=prefix), package] |
+ if as_name is not None: |
+ children.extend([Name(u"as", prefix=u" "), |
+ Name(as_name, prefix=u" ")]) |
+ return Node(syms.import_name, children) |
+ |
+_compound_stmts = (syms.if_stmt, syms.while_stmt, syms.for_stmt, syms.try_stmt, syms.with_stmt) |
+_import_stmts = (syms.import_name, syms.import_from) |
+ |
+def import_binding_scope(node): |
+ """ |
+ Generator yields all nodes for which a node (an import_stmt) has scope |
+ The purpose of this is for a call to _find() on each of them |
+ """ |
+ # import_name / import_from are small_stmts |
+ assert node.type in _import_stmts |
+ test = node.next_sibling |
+ # A small_stmt can only be followed by a SEMI or a NEWLINE. |
+ while test.type == token.SEMI: |
+ nxt = test.next_sibling |
+ # A SEMI can only be followed by a small_stmt or a NEWLINE |
+ if nxt.type == token.NEWLINE: |
+ break |
+ else: |
+ yield nxt |
+ # A small_stmt can only be followed by either a SEMI or a NEWLINE |
+ test = nxt.next_sibling |
+ # Covered all subsequent small_stmts after the import_stmt |
+ # Now to cover all subsequent stmts after the parent simple_stmt |
+ parent = node.parent |
+ assert parent.type == syms.simple_stmt |
+ test = parent.next_sibling |
+ while test is not None: |
+ # Yes, this will yield NEWLINE and DEDENT. Deal with it. |
+ yield test |
+ test = test.next_sibling |
+ |
+ context = parent.parent |
+ # Recursively yield nodes following imports inside of a if/while/for/try/with statement |
+ if context.type in _compound_stmts: |
+ # import is in a one-liner |
+ c = context |
+ while c.next_sibling is not None: |
+ yield c.next_sibling |
+ c = c.next_sibling |
+ context = context.parent |
+ |
+ # Can't chain one-liners on one line, so that takes care of that. |
+ |
+ p = context.parent |
+ if p is None: |
+ return |
+ |
+ # in a multi-line suite |
+ |
+ while p.type in _compound_stmts: |
+ |
+ if context.type == syms.suite: |
+ yield context |
+ |
+ context = context.next_sibling |
+ |
+ if context is None: |
+ context = p.parent |
+ p = context.parent |
+ if p is None: |
+ break |
+ |
+def ImportAsName(name, as_name, prefix=None): |
+ new_name = Name(name) |
+ new_as = Name(u"as", prefix=u" ") |
+ new_as_name = Name(as_name, prefix=u" ") |
+ new_node = Node(syms.import_as_name, [new_name, new_as, new_as_name]) |
+ if prefix is not None: |
+ new_node.prefix = prefix |
+ return new_node |
+ |
+ |
+def is_docstring(node): |
+ """ |
+ Returns True if the node appears to be a docstring |
+ """ |
+ return (node.type == syms.simple_stmt and |
+ len(node.children) > 0 and node.children[0].type == token.STRING) |
+ |
+ |
+def future_import(feature, node): |
+ """ |
+ This seems to work |
+ """ |
+ root = find_root(node) |
+ |
+ if does_tree_import(u"__future__", feature, node): |
+ return |
+ |
+ # Look for a shebang or encoding line |
+ shebang_encoding_idx = None |
+ |
+ for idx, node in enumerate(root.children): |
+ # Is it a shebang or encoding line? |
+ if is_shebang_comment(node) or is_encoding_comment(node): |
+ shebang_encoding_idx = idx |
+ if is_docstring(node): |
+ # skip over docstring |
+ continue |
+ names = check_future_import(node) |
+ if not names: |
+ # not a future statement; need to insert before this |
+ break |
+ if feature in names: |
+ # already imported |
+ return |
+ |
+ import_ = FromImport(u'__future__', [Leaf(token.NAME, feature, prefix=" ")]) |
+ if shebang_encoding_idx == 0 and idx == 0: |
+ # If this __future__ import would go on the first line, |
+ # detach the shebang / encoding prefix from the current first line. |
+ # and attach it to our new __future__ import node. |
+ import_.prefix = root.children[0].prefix |
+ root.children[0].prefix = u'' |
+ # End the __future__ import line with a newline and add a blank line |
+ # afterwards: |
+ children = [import_ , Newline()] |
+ root.insert_child(idx, Node(syms.simple_stmt, children)) |
+ |
+ |
+def future_import2(feature, node): |
+ """ |
+ An alternative to future_import() which might not work ... |
+ """ |
+ root = find_root(node) |
+ |
+ if does_tree_import(u"__future__", feature, node): |
+ return |
+ |
+ insert_pos = 0 |
+ for idx, node in enumerate(root.children): |
+ if node.type == syms.simple_stmt and node.children and \ |
+ node.children[0].type == token.STRING: |
+ insert_pos = idx + 1 |
+ break |
+ |
+ for thing_after in root.children[insert_pos:]: |
+ if thing_after.type == token.NEWLINE: |
+ insert_pos += 1 |
+ continue |
+ |
+ prefix = thing_after.prefix |
+ thing_after.prefix = u"" |
+ break |
+ else: |
+ prefix = u"" |
+ |
+ import_ = FromImport(u"__future__", [Leaf(token.NAME, feature, prefix=u" ")]) |
+ |
+ children = [import_, Newline()] |
+ root.insert_child(insert_pos, Node(syms.simple_stmt, children, prefix=prefix)) |
+ |
+def parse_args(arglist, scheme): |
+ u""" |
+ Parse a list of arguments into a dict |
+ """ |
+ arglist = [i for i in arglist if i.type != token.COMMA] |
+ |
+ ret_mapping = dict([(k, None) for k in scheme]) |
+ |
+ for i, arg in enumerate(arglist): |
+ if arg.type == syms.argument and arg.children[1].type == token.EQUAL: |
+ # argument < NAME '=' any > |
+ slot = arg.children[0].value |
+ ret_mapping[slot] = arg.children[2] |
+ else: |
+ slot = scheme[i] |
+ ret_mapping[slot] = arg |
+ |
+ return ret_mapping |
+ |
+ |
+# def is_import_from(node): |
+# """Returns true if the node is a statement "from ... import ..." |
+# """ |
+# return node.type == syms.import_from |
+ |
+ |
+def is_import_stmt(node): |
+ return (node.type == syms.simple_stmt and node.children and |
+ is_import(node.children[0])) |
+ |
+ |
+def touch_import_top(package, name_to_import, node): |
+ """Works like `does_tree_import` but adds an import statement at the |
+ top if it was not imported (but below any __future__ imports) and below any |
+ comments such as shebang lines). |
+ |
+ Based on lib2to3.fixer_util.touch_import() |
+ |
+ Calling this multiple times adds the imports in reverse order. |
+ |
+ Also adds "standard_library.install_aliases()" after "from future import |
+ standard_library". This should probably be factored into another function. |
+ """ |
+ |
+ root = find_root(node) |
+ |
+ if does_tree_import(package, name_to_import, root): |
+ return |
+ |
+ # Ideally, we would look for whether futurize --all-imports has been run, |
+ # as indicated by the presence of ``from builtins import (ascii, ..., |
+ # zip)`` -- and, if it has, we wouldn't import the name again. |
+ |
+ # Look for __future__ imports and insert below them |
+ found = False |
+ for name in ['absolute_import', 'division', 'print_function', |
+ 'unicode_literals']: |
+ if does_tree_import('__future__', name, root): |
+ found = True |
+ break |
+ if found: |
+ # At least one __future__ import. We want to loop until we've seen them |
+ # all. |
+ start, end = None, None |
+ for idx, node in enumerate(root.children): |
+ if check_future_import(node): |
+ start = idx |
+ # Start looping |
+ idx2 = start |
+ while node: |
+ node = node.next_sibling |
+ idx2 += 1 |
+ if not check_future_import(node): |
+ end = idx2 |
+ break |
+ break |
+ assert start is not None |
+ assert end is not None |
+ insert_pos = end |
+ else: |
+ # No __future__ imports. |
+ # We look for a docstring and insert the new node below that. If no docstring |
+ # exists, just insert the node at the top. |
+ for idx, node in enumerate(root.children): |
+ if node.type != syms.simple_stmt: |
+ break |
+ if not is_docstring(node): |
+ # This is the usual case. |
+ break |
+ insert_pos = idx |
+ |
+ if package is None: |
+ import_ = Node(syms.import_name, [ |
+ Leaf(token.NAME, u"import"), |
+ Leaf(token.NAME, name_to_import, prefix=u" ") |
+ ]) |
+ else: |
+ import_ = FromImport(package, [Leaf(token.NAME, name_to_import, prefix=u" ")]) |
+ if name_to_import == u'standard_library': |
+ # Add: |
+ # standard_library.install_aliases() |
+ # after: |
+ # from future import standard_library |
+ install_hooks = Node(syms.simple_stmt, |
+ [Node(syms.power, |
+ [Leaf(token.NAME, u'standard_library'), |
+ Node(syms.trailer, [Leaf(token.DOT, u'.'), |
+ Leaf(token.NAME, u'install_aliases')]), |
+ Node(syms.trailer, [Leaf(token.LPAR, u'('), |
+ Leaf(token.RPAR, u')')]) |
+ ]) |
+ ] |
+ ) |
+ children_hooks = [install_hooks, Newline()] |
+ else: |
+ children_hooks = [] |
+ |
+ # FromImport(package, [Leaf(token.NAME, name_to_import, prefix=u" ")]) |
+ |
+ children_import = [import_, Newline()] |
+ old_prefix = root.children[insert_pos].prefix |
+ root.children[insert_pos].prefix = u'' |
+ root.insert_child(insert_pos, Node(syms.simple_stmt, children_import, prefix=old_prefix)) |
+ if len(children_hooks) > 0: |
+ root.insert_child(insert_pos + 1, Node(syms.simple_stmt, children_hooks)) |
+ |
+ |
+## The following functions are from python-modernize by Armin Ronacher: |
+# (a little edited). |
+ |
+def check_future_import(node): |
+ """If this is a future import, return set of symbols that are imported, |
+ else return None.""" |
+ # node should be the import statement here |
+ savenode = node |
+ if not (node.type == syms.simple_stmt and node.children): |
+ return set() |
+ node = node.children[0] |
+ # now node is the import_from node |
+ if not (node.type == syms.import_from and |
+ # node.type == token.NAME and # seems to break it |
+ hasattr(node.children[1], 'value') and |
+ node.children[1].value == u'__future__'): |
+ return set() |
+ node = node.children[3] |
+ # now node is the import_as_name[s] |
+ # print(python_grammar.number2symbol[node.type]) # breaks sometimes |
+ if node.type == syms.import_as_names: |
+ result = set() |
+ for n in node.children: |
+ if n.type == token.NAME: |
+ result.add(n.value) |
+ elif n.type == syms.import_as_name: |
+ n = n.children[0] |
+ assert n.type == token.NAME |
+ result.add(n.value) |
+ return result |
+ elif node.type == syms.import_as_name: |
+ node = node.children[0] |
+ assert node.type == token.NAME |
+ return set([node.value]) |
+ elif node.type == token.NAME: |
+ return set([node.value]) |
+ else: |
+ # TODO: handle brackets like this: |
+ # from __future__ import (absolute_import, division) |
+ assert False, "strange import: %s" % savenode |
+ |
+ |
+SHEBANG_REGEX = r'^#!.*python' |
+ENCODING_REGEX = r"^#.*coding[:=]\s*([-\w.]+)" |
+ |
+ |
+def is_shebang_comment(node): |
+ """ |
+ Comments are prefixes for Leaf nodes. Returns whether the given node has a |
+ prefix that looks like a shebang line or an encoding line: |
+ |
+ #!/usr/bin/env python |
+ #!/usr/bin/python3 |
+ """ |
+ return bool(re.match(SHEBANG_REGEX, node.prefix)) |
+ |
+ |
+def is_encoding_comment(node): |
+ """ |
+ Comments are prefixes for Leaf nodes. Returns whether the given node has a |
+ prefix that looks like an encoding line: |
+ |
+ # coding: utf-8 |
+ # encoding: utf-8 |
+ # -*- coding: <encoding name> -*- |
+ # vim: set fileencoding=<encoding name> : |
+ """ |
+ return bool(re.match(ENCODING_REGEX, node.prefix)) |
+ |
+ |
+def wrap_in_fn_call(fn_name, args, prefix=None): |
+ """ |
+ Example: |
+ >>> wrap_in_fn_call("oldstr", (arg,)) |
+ oldstr(arg) |
+ |
+ >>> wrap_in_fn_call("olddiv", (arg1, arg2)) |
+ olddiv(arg1, arg2) |
+ """ |
+ assert len(args) > 0 |
+ if len(args) == 1: |
+ newargs = args |
+ elif len(args) == 2: |
+ expr1, expr2 = args |
+ newargs = [expr1, Comma(), expr2] |
+ else: |
+ assert NotImplementedError('write me') |
+ return Call(Name(fn_name), newargs, prefix=prefix) |
+ |
+ |