Beispiel #1
0
    def test_copy(self):
        node_1 = ast.Name()
        anno.setanno(node_1, 'foo', 3)

        node_2 = ast.Name()
        anno.copyanno(node_1, node_2, 'foo')
        anno.copyanno(node_1, node_2, 'bar')

        self.assertTrue(anno.hasanno(node_2, 'foo'))
        self.assertFalse(anno.hasanno(node_2, 'bar'))
Beispiel #2
0
    def test_duplicate(self):
        node = ast.If(test=ast.Num(1),
                      body=[ast.Expr(ast.Name('bar', ast.Load()))],
                      orelse=[])
        anno.setanno(node, 'spam', 1)
        anno.setanno(node, 'ham', 1)
        anno.setanno(node.body[0], 'ham', 1)

        anno.dup(node, {'spam': 'eggs'})

        self.assertTrue(anno.hasanno(node, 'spam'))
        self.assertTrue(anno.hasanno(node, 'ham'))
        self.assertTrue(anno.hasanno(node, 'eggs'))
        self.assertFalse(anno.hasanno(node.body[0], 'eggs'))
Beispiel #3
0
 def visit_Attribute(self, node):
     node = self.generic_visit(node)
     if anno.hasanno(node.value, anno.Basic.QN):
         anno.setanno(
             node, anno.Basic.QN,
             QN(anno.getanno(node.value, anno.Basic.QN), attr=node.attr))
     return node
Beispiel #4
0
 def _node_sets_self_attribute(self, node):
     if anno.hasanno(node, anno.Basic.QN):
         qn = anno.getanno(node, anno.Basic.QN)
         # TODO(mdanatg): The 'self' argument is not guaranteed to be called 'self'.
         if qn.has_attr and qn.parent.qn == ('self', ):
             return True
     return False
Beispiel #5
0
    def _track_symbol(self, node, composite_writes_alter_parent=False):
        # A QN may be missing when we have an attribute (or subscript) on a function
        # call. Example: a().b
        if not anno.hasanno(node, anno.Basic.QN):
            return
        qn = anno.getanno(node, anno.Basic.QN)

        # When inside a lambda, ignore any of the lambda's arguments.
        # This includes attributes or slices of those arguments.
        for l in self.state[_Lambda]:
            if qn in l.args:
                return
            if qn.owner_set & set(l.args):
                return

        # When inside a comprehension, ignore any of the comprehensions's targets.
        # This includes attributes or slices of those arguments.
        # This is not true in Python2, which leaks symbols.
        if six.PY3:
            for l in self.state[_Comprehension]:
                if qn in l.targets:
                    return
                if qn.owner_set & set(l.targets):
                    return

        if isinstance(node.ctx, gast.Store):
            # In comprehensions, modified symbols are the comprehension targets.
            if six.PY3 and self.state[_Comprehension].level > 0:
                # Like a lambda's args, they are tracked separately in Python3.
                self.state[_Comprehension].targets.add(qn)
            else:
                self.scope.mark_modified(qn)
                if qn.is_composite and composite_writes_alter_parent:
                    self.scope.mark_modified(qn.parent)
                if self._in_aug_assign:
                    self.scope.mark_read(qn)
        elif isinstance(node.ctx, gast.Load):
            self.scope.mark_read(qn)
        elif isinstance(node.ctx, gast.Param):
            if self._in_function_def_args:
                # In function defs have the meaning of defining a variable.
                self.scope.mark_modified(qn)
                self.scope.mark_param(qn, self.enclosing_entities[-1])
            elif self.state[_Lambda].level:
                # In lambdas, they are tracked separately.
                self.state[_Lambda].args.add(qn)
            else:
                # TODO(mdanatg): Is this case possible at all?
                raise NotImplementedError(
                    'Param "{}" outside a function arguments or lambda.'.
                    format(qn))
        elif isinstance(node.ctx, gast.Del):
            # The read matches the Python semantics - attempting to delete an
            # undefined symbol is illegal.
            self.scope.mark_read(qn)
            self.scope.mark_deleted(qn)
        else:
            raise ValueError('Unknown context {} for node "{}".'.format(
                type(node.ctx), qn))
Beispiel #6
0
 def test_copy_clean_preserves_annotations(self):
   node = parsing.parse_str(
       textwrap.dedent("""
     def f(a):
       return a + 1
   """))
   anno.setanno(node.body[0], 'foo', 'bar')
   anno.setanno(node.body[0], 'baz', 1)
   new_node = ast_util.copy_clean(node, preserve_annos={'foo'})
   self.assertEqual(anno.getanno(new_node.body[0], 'foo'), 'bar')
   self.assertFalse(anno.hasanno(new_node.body[0], 'baz'))
Beispiel #7
0
    def test_basic(self):
        node = ast.Name()

        self.assertEqual(anno.keys(node), set())
        self.assertFalse(anno.hasanno(node, 'foo'))
        with self.assertRaises(AttributeError):
            anno.getanno(node, 'foo')

        anno.setanno(node, 'foo', 3)

        self.assertEqual(anno.keys(node), {'foo'})
        self.assertTrue(anno.hasanno(node, 'foo'))
        self.assertEqual(anno.getanno(node, 'foo'), 3)
        self.assertEqual(anno.getanno(node, 'bar', default=7), 7)

        anno.delanno(node, 'foo')

        self.assertEqual(anno.keys(node), set())
        self.assertFalse(anno.hasanno(node, 'foo'))
        with self.assertRaises(AttributeError):
            anno.getanno(node, 'foo')
        self.assertIsNone(anno.getanno(node, 'foo', default=None))
Beispiel #8
0
 def visit_Subscript(self, node):
     # TODO(mdanatg): This may no longer apply if we overload getitem.
     node = self.generic_visit(node)
     s = node.slice
     if not isinstance(s, gast.Index):
         # TODO(mdanatg): Support range and multi-dimensional indices.
         # Continuing silently because some demos use these.
         return node
     if isinstance(s.value, gast.Num):
         subscript = QN(NumberLiteral(s.value.n))
     elif isinstance(s.value, gast.Str):
         subscript = QN(StringLiteral(s.value.s))
     else:
         # The index may be an expression, case in which a name doesn't make sense.
         if anno.hasanno(node.slice.value, anno.Basic.QN):
             subscript = anno.getanno(node.slice.value, anno.Basic.QN)
         else:
             return node
     if anno.hasanno(node.value, anno.Basic.QN):
         anno.setanno(
             node, anno.Basic.QN,
             QN(anno.getanno(node.value, anno.Basic.QN),
                subscript=subscript))
     return node
Beispiel #9
0
    def visit(self, node):
        if not isinstance(node, gast.AST):
            # This is not that uncommon a mistake: various node bodies are lists, for
            # example, posing a land mine for transformers that need to recursively
            # call `visit`.  The error needs to be raised before the exception handler
            # below is installed, because said handler will mess up if `node` is not,
            # in fact, a node.
            msg = ('invalid value for "node": expected "ast.AST", got "{}"; to'
                   ' visit lists of nodes, use "visit_block" instead').format(
                       type(node))
            raise ValueError(msg)

        did_enter_function = False
        processing_expr_node = False

        if isinstance(node, (gast.FunctionDef, gast.ClassDef, gast.Lambda)):
            did_enter_function = True
        elif isinstance(node, gast.Expr):
            processing_expr_node = True

        if did_enter_function:
            self._enclosing_entities.append(node)

        if processing_expr_node:
            entry_expr_value = node.value

        if not anno.hasanno(node, anno.Basic.SKIP_PROCESSING):
            result = super(Base, self).visit(node)

        # Adjust for consistency: replacing the value of an Expr with
        # an Assign node removes the need for the Expr node.
        if processing_expr_node:
            if isinstance(result,
                          gast.Expr) and result.value != entry_expr_value:
                # When the replacement is a list, it is assumed that the list came
                # from a template that contained a number of statements, which
                # themselves are standalone and don't require an enclosing Expr.
                if isinstance(result.value,
                              (list, tuple, gast.Assign, gast.AugAssign)):
                    result = result.value

        # On exception, the local scope integrity is not guaranteed.
        if did_enter_function:
            self._enclosing_entities.pop()

        return result
Beispiel #10
0
 def visit_Attribute(self, node):
     if anno.hasanno(node, anno.Basic.QN):
         return self._process(node)
     # Attributes of dynamic objects will not have a QN.
     return self.generic_visit(node)