Exemplo n.º 1
0
 def test_call_no_pos(self):
     """Tests that Call node traversal works without position information."""
     src = 'f(a)'
     t = pasta.parse(src, py_ver)
     node = ast_utils.find_nodes_by_type(t, (ast27.Call, ast3.Call),
                                         py_ver)[0]
     node.keywords.append(
         pasta.ast(py_ver).keyword(arg='b',
                                   value=pasta.ast(py_ver).Num(n=0)))
     self.assertEqual('f(a, b=0)', pasta.dump(t, py_ver))
Exemplo n.º 2
0
        def test_call_illegal_pos(self):
            """Tests that Call node traversal works even with illegal positions."""
            src = 'f(a)'
            t = pasta.parse(src, py_ver)
            node = ast_utils.find_nodes_by_type(t, (ast27.Call, ast3.Call),
                                                py_ver)[0]
            node.keywords.append(
                pasta.ast(py_ver).keyword(arg='b',
                                          value=pasta.ast(py_ver).Num(n=0)))

            # This position would put b=0 before a, so it should be ignored.
            node.keywords[-1].value.lineno = 0
            node.keywords[-1].value.col_offset = 0

            self.assertEqual('f(a, b=0)', pasta.dump(t, py_ver))
Exemplo n.º 3
0
    def test_try_nested_imports(self):
      source = textwrap.dedent("""\
          try:
            import aaa
          except:
            import bbb
          finally:
            import ccc
          """)
      tree = pasta.ast_parse(source, py_ver)
      nodes = tree.body

      node_aaa, node_bbb, node_ccc = ast_utils.find_nodes_by_type(
          tree, pasta.ast(py_ver).alias, py_ver)

      s = scope.analyze(tree, py_ver)

      self.assertItemsEqual(s.names.keys(), {'aaa', 'bbb', 'ccc'})
      self.assertItemsEqual(s.external_references.keys(), {'aaa', 'bbb', 'ccc'})

      self.assertEqual(s.names['aaa'].definition, node_aaa)
      self.assertEqual(s.names['bbb'].definition, node_bbb)
      self.assertEqual(s.names['ccc'].definition, node_ccc)

      for ref in {'aaa', 'bbb', 'ccc'}:
        self.assertEqual(s.names[ref].reads, [],
                         'Expected no reads for %s' % ref)
Exemplo n.º 4
0
  class _TreeNormalizer(pasta.ast(py_ver).NodeTransformer):
    """Replaces all op nodes with unique instances."""

    def visit(self, node):
      if isinstance(node, _AST_OP_NODES):
        return node.__class__()
      return super(_TreeNormalizer, self).visit(node)
Exemplo n.º 5
0
 def test_autoindent(self):
     src = textwrap.dedent("""\
   def a():
       b
       c
   """)
     expected = textwrap.dedent("""\
   def a():
       b
       new_node
   """)
     t = pasta.parse(src, py_ver)
     # Repace the second node and make sure the indent level is corrected
     t.body[0].body[1] = pasta.ast(py_ver).Expr(
         pasta.ast(py_ver).Name(id='new_node'))
     self.assertMultiLineEqual(expected, codegen.to_str(t, py_ver))
Exemplo n.º 6
0
def remove_child(parent, child, py_ver=sys.version_info[:2]):

  for _, field_value in pasta.ast(py_ver).iter_fields(parent):
    if isinstance(field_value, list) and child in field_value:
      field_value.remove(child)
      return
  raise errors.InvalidAstError('Unable to find list containing child %r on '
                               'parent node %r' % (child, parent))
Exemplo n.º 7
0
  class FindNodeVisitor(pasta.ast(py_ver).NodeVisitor):

    def __init__(self, condition):
      self._condition = condition
      self.results = []

    def visit(self, node):
      if self._condition(node):
        self.results.append(node)
      super(FindNodeVisitor, self).visit(node)
Exemplo n.º 8
0
 def make_safe_alias_node(alias_name, asname):
   # Try to avoid name conflicts
   new_alias = pasta.ast(py_ver).alias(name=alias_name, asname=asname)
   imported_name = asname or alias_name
   counter = 0
   while imported_name in sc.names:
     counter += 1
     imported_name = new_alias.asname = '%s_%d' % (asname or alias_name, 
                                                   counter)
   return new_alias
Exemplo n.º 9
0
        def test_split_from_import(self):
            src = 'from aaa import bbb, ccc, ddd\n'
            t = pasta.ast_parse(src, py_ver)
            import_node = t.body[0]
            sc = scope.analyze(t, py_ver)
            import_utils.split_import(sc, import_node, import_node.names[1])

            self.assertEqual(2, len(t.body))
            self.assertEqual(pasta.ast(py_ver).ImportFrom, type(t.body[1]))
            self.assertEqual(t.body[0].module, 'aaa')
            self.assertEqual(t.body[1].module, 'aaa')
            self.assertEqual([alias.name for alias in t.body[0].names],
                             ['bbb', 'ddd'])
Exemplo n.º 10
0
        def test_split_normal_import(self):
            src = 'import aaa, bbb, ccc\n'
            t = pasta.ast_parse(src, py_ver)
            import_node = t.body[0]
            sc = scope.analyze(t, py_ver)
            import_utils.split_import(sc, import_node, import_node.names[1])

            self.assertEqual(2, len(t.body))
            self.assertEqual(pasta.ast(py_ver).Import, type(t.body[1]))
            self.assertEqual([alias.name for alias in t.body[0].names],
                             ['aaa', 'ccc'])
            self.assertEqual([alias.name for alias in t.body[1].names],
                             ['bbb'])
Exemplo n.º 11
0
    def test_functiondef_nested_imports(self):
      source = textwrap.dedent("""\
          def foo(bar):
            import aaa
          """)
      tree = pasta.ast_parse(source, py_ver)
      nodes = tree.body

      node_aaa = ast_utils.find_nodes_by_type(
          tree, pasta.ast(py_ver).alias, py_ver)[0]

      s = scope.analyze(tree, py_ver)

      self.assertItemsEqual(s.names.keys(), {'foo'})
      self.assertItemsEqual(s.external_references.keys(), {'aaa'})
Exemplo n.º 12
0
        def test_remove_just_alias_import_from(self):
            src = 'from m import a, b'
            tree = pasta.ast_parse(src, py_ver)
            sc = scope.analyze(tree, py_ver)

            unused_b_node = tree.body[0].names[1]

            import_utils.remove_import_alias_node(sc,
                                                  unused_b_node,
                                                  py_ver=py_ver)

            self.assertEqual(len(tree.body), 1)
            self.assertEqual(type(tree.body[0]), pasta.ast(py_ver).ImportFrom)
            self.assertEqual(len(tree.body[0].names), 1)
            self.assertEqual(tree.body[0].names[0].name, 'a')
Exemplo n.º 13
0
def parse(src, py_ver=sys.version_info[:2]):
  """Replaces typed_ast.parse; ensures additional properties on the parsed tree.

  This enforces the assumption that each node in the ast is unique.
  """

  class _TreeNormalizer(pasta.ast(py_ver).NodeTransformer):
    """Replaces all op nodes with unique instances."""

    def visit(self, node):
      if isinstance(node, _AST_OP_NODES):
        return node.__class__()
      return super(_TreeNormalizer, self).visit(node)

  tree=pasta.ast(py_ver).parse(sanitize_source(src))
  _TreeNormalizer().visit(tree)
  return tree
Exemplo n.º 14
0
    def test_lookup_scope(self):
      src = textwrap.dedent("""\
          import a
          def b(c, d, e=1):
            class F(d):
              g = 1
            return c
          """)
      t = pasta.ast_parse(src, py_ver)
      import_node, func_node = t.body
      class_node, return_node = func_node.body

      sc = scope.analyze(t, py_ver)
      import_node_scope = sc.lookup_scope(import_node)
      self.assertIs(import_node_scope.node, t)
      self.assertIs(import_node_scope, sc)
      self.assertItemsEqual(import_node_scope.names, ['a', 'b'])

      func_node_scope = sc.lookup_scope(func_node)
      self.assertIs(func_node_scope.node, func_node)
      self.assertIs(func_node_scope.parent_scope, sc)
      self.assertItemsEqual(func_node_scope.names, ['c', 'd', 'e', 'F'])

      class_node_scope = sc.lookup_scope(class_node)
      self.assertIs(class_node_scope.node, class_node)
      self.assertIs(class_node_scope.parent_scope, func_node_scope)
      self.assertItemsEqual(class_node_scope.names, ['g'])

      return_node_scope = sc.lookup_scope(return_node)
      self.assertIs(return_node_scope.node, func_node)
      self.assertIs(return_node_scope, func_node_scope)
      self.assertItemsEqual(return_node_scope.names, ['c', 'd', 'e', 'F'])

      self.assertIs(class_node_scope.lookup_scope(func_node), func_node_scope)

      self.assertIsNone(sc.lookup_scope(pasta.ast(py_ver).Name(id='foo')))
Exemplo n.º 15
0
    class ScopeVisitor(pasta.ast(py_ver).NodeVisitor):
        def __init__(self):
            super(ScopeVisitor, self).__init__()
            self._parent = None
            self.root_scope = self.scope = RootScope(None)

        def visit(self, node):
            if node is None:
                return
            if self.root_scope.node is None:
                self.root_scope.node = node
            self.root_scope.set_parent(node, self._parent)
            tmp = self._parent
            self._parent = node
            super(ScopeVisitor, self).visit(node)
            self._parent = tmp

        def visit_in_order(self, node, *attrs):
            for attr in attrs:
                val = getattr(node, attr, None)
                if val is None:
                    continue
                if isinstance(val, list):
                    for item in val:
                        self.visit(item)
                elif isinstance(val, (ast27.AST, ast3.AST)):
                    self.visit(val)

        def visit_Import(self, node):
            for alias in node.names:
                name_parts = alias.name.split('.')

                if not alias.asname:
                    # If not aliased, define the top-level module of the import
                    cur_name = self.scope.define_name(name_parts[0], alias)
                    self.root_scope.add_external_reference(name_parts[0],
                                                           alias,
                                                           name_ref=cur_name)

                    # Define names of sub-modules imported
                    partial_name = name_parts[0]
                    for part in name_parts[1:]:
                        partial_name += '.' + part
                        cur_name = cur_name.lookup_name(part)
                        cur_name.define(alias)
                        self.root_scope.add_external_reference(
                            partial_name, alias, name_ref=cur_name)

                else:
                    # If the imported name is aliased, define that name only
                    name = self.scope.define_name(alias.asname, alias)

                    # Define names of sub-modules imported
                    for i in range(1, len(name_parts)):
                        self.root_scope.add_external_reference(
                            '.'.join(name_parts[:i]), alias)
                    self.root_scope.add_external_reference(alias.name,
                                                           alias,
                                                           name_ref=name)

            self.generic_visit(node)

        def visit_ImportFrom(self, node):
            if node.module:
                name_parts = node.module.split('.')
                for i in range(1, len(name_parts) + 1):
                    self.root_scope.add_external_reference(
                        '.'.join(name_parts[:i]), node)
            for alias in node.names:
                name = self.scope.define_name(alias.asname or alias.name,
                                              alias)
                if node.module:
                    self.root_scope.add_external_reference('.'.join(
                        (node.module, alias.name)),
                                                           alias,
                                                           name_ref=name)
                # TODO: else? relative imports
            self.generic_visit(node)

        def visit_Name(self, node):
            if isinstance(node.ctx,
                          (ast27.Store, ast3.Store, ast27.Param, ast3.Param)):
                self.scope.define_name(node.id, node)
            elif isinstance(node.ctx, (ast27.Load, ast3.Load)):
                self.scope.lookup_name(node.id).add_reference(node)
                self.root_scope.set_name_for_node(
                    node, self.scope.lookup_name(node.id))
            self.generic_visit(node)

        def visit_FunctionDef(self, node):
            # Visit decorator list first to avoid declarations in args
            self.visit_in_order(node, 'decorator_list')
            if isinstance(self.root_scope.parent(node),
                          (ast27.ClassDef, ast3.ClassDef)):
                pass  # TODO: Support referencing methods by "self" where possible
            else:
                self.scope.define_name(node.name, node)
            try:
                self.scope = self.scope.create_scope(node)

                self.visit_in_order(node, 'args', 'returns', 'body')
            finally:
                self.scope = self.scope.parent_scope

        def visit_arguments(self, node):
            self.visit_in_order(node, 'defaults', 'args')
            if py_ver < (3, 0):
                # In python 2.x, these names are not Name nodes. Define them explicitly
                # to be able to find references in the function body.
                for arg_attr_name in ('vararg', 'kwarg'):
                    arg_name = getattr(node, arg_attr_name, None)
                    if arg_name is not None:
                        self.scope.define_name(arg_name, node)
            else:
                # Visit defaults first to avoid declarations in args
                self.visit_in_order(node, 'vararg', 'kwarg')

        def visit_arg(self, node):
            self.scope.define_name(node.arg, node)

            # PEP 484 forward reference type annotations
            if hasattr(node, 'annotation') and isinstance(
                    node.annotation, (ast27.Str, ast3.Str)):
                name_parts = node.annotation.s.split('.')
                # TODO: Fix this; the name may not be defined in the root scope.
                name = self.root_scope.forward_define_name(
                    name_parts[0], node.annotation)
                for part in name_parts[1:]:
                    name = name.lookup_name(part)
                    name.add_reference(node.annotation)
            self.generic_visit(node)

        def visit_ClassDef(self, node):
            self.visit_in_order(node, 'decorator_list', 'bases')
            self.scope.define_name(node.name, node)
            try:
                self.scope = self.scope.create_scope(node)
                self.visit_in_order(node, 'body')
            finally:
                self.scope = self.scope.parent_scope

        def visit_Attribute(self, node):
            self.generic_visit(node)
            node_value_name = self.root_scope.get_name_for_node(node.value)
            if node_value_name:
                node_name = node_value_name.lookup_name(node.attr)
                self.root_scope.set_name_for_node(node, node_name)
                node_name.add_reference(node)
Exemplo n.º 16
0
def add_import(tree, name_to_import, asname=None, from_import=True, merge_from_imports=True,
               py_ver=sys.version_info[:2]):
  """Adds an import to the module.
  
  This function will try to ensure not to create duplicate imports. If name_to_import is
  already imported, it will return the existing import. This is true even if asname is set
  (asname will be ignored, and the existing name will be returned).
  
  If the import would create a name that already exists in the scope given by tree, this
  function will "import as", and append "_x" to the asname where x is the smallest positive
  integer generating a unique name.

  Arguments:
    tree: (ast.Module) Module AST to modify.
    name_to_import: (string) The absolute name to import.
    asname: (string) The alias for the import ("import name_to_import as asname")
    from_import: (boolean) If True, import the name using an ImportFrom node.
    merge_from_imports: (boolean) If True, merge a newly inserted ImportFrom
      node into an existing ImportFrom node, if applicable.

  Returns:
    The name (as a string) that can be used to reference the imported name. This
      can be the fully-qualified name, the basename, or an alias name.
  """
  sc = scope.analyze(tree, py_ver=py_ver)

  # Don't add anything if it's already imported
  if name_to_import in sc.external_references:
    existing_ref = next((ref for ref in sc.external_references[name_to_import]
                         if ref.name_ref is not None), None)
    if existing_ref:
      return existing_ref.name_ref.id

  import_node = None
  added_name = None

  def make_safe_alias_node(alias_name, asname):
    # Try to avoid name conflicts
    new_alias = pasta.ast(py_ver).alias(name=alias_name, asname=asname)
    imported_name = asname or alias_name
    counter = 0
    while imported_name in sc.names:
      counter += 1
      imported_name = new_alias.asname = '%s_%d' % (asname or alias_name, 
                                                    counter)
    return new_alias

  # Add an ImportFrom node if requested and possible
  if from_import and '.' in name_to_import:
    from_module, alias_name = name_to_import.rsplit('.', 1)

    new_alias = make_safe_alias_node(alias_name, asname)

    if merge_from_imports:
      # Try to add to an existing ImportFrom from the same module
      existing_from_import = next(
          (node for node in tree.body
           if isinstance(node, (ast27.ImportFrom, ast3.ImportFrom)) and
           node.module == from_module and node.level == 0), None)
      if existing_from_import:
        existing_from_import.names.append(new_alias)
        return new_alias.asname or new_alias.name

    # Create a new node for this import
    import_node = pasta.ast(py_ver).ImportFrom(
        module=from_module, names=[new_alias], level=0)

  # If not already created as an ImportFrom, create a normal Import node
  if not import_node:
    new_alias = make_safe_alias_node(alias_name=name_to_import, asname=asname)
    import_node = pasta.ast(py_ver).Import(names=[new_alias])

  # Insert the node at the top of the module and return the name in scope
  tree.body.insert(1 if ast_utils.has_docstring(tree) else 0, import_node)
  return new_alias.asname or new_alias.name