コード例 #1
0
ファイル: ast_util_test.py プロジェクト: google/pyctr
 def test_parallel_walk_inconsistent_trees(self):
   node_1 = parsing.parse_str(
       textwrap.dedent("""
     def f(a):
       return a + 1
   """))
   node_2 = parsing.parse_str(
       textwrap.dedent("""
     def f(a):
       return a + (a * 2)
   """))
   node_3 = parsing.parse_str(
       textwrap.dedent("""
     def f(a):
       return a + 2
   """))
   with self.assertRaises(ValueError):
     for _ in ast_util.parallel_walk(node_1, node_2):
       pass
   # There is not particular reason to reject trees that differ only in the
   # value of a constant.
   # TODO(mdanatg): This should probably be allowed.
   with self.assertRaises(ValueError):
     for _ in ast_util.parallel_walk(node_1, node_3):
       pass
コード例 #2
0
ファイル: templates.py プロジェクト: google/pyctr
def replace(template, **replacements):
  """Replaces placeholders in a Python template.

  AST Name and Tuple nodes always receive the context that inferred from
  the template. However, when replacing more complex nodes (that can potentially
  contain Name children), then the caller is responsible for setting the
  appropriate context.

  Args:
    template: A string representing Python code. Any symbol name can be used
      that appears in the template code can be used as placeholder.
    **replacements: A mapping from placeholder names to (lists of) AST nodes
      that these placeholders will be replaced by. String values are also
      supported as a shorthand for AST Name nodes with the respective ID.

  Returns:
    An AST node or list of AST nodes with the replacements made. If the
    template was a function, a list will be returned. If the template was a
    node, the same node will be returned. If the template was a string, an
    AST node will be returned (a `Module` node in the case of a multi-line
    string, an `Expr` node otherwise).

  Raises:
    ValueError: if the arguments are incorrect.
  """
  if not isinstance(template, str):
    raise ValueError('Expected string template, got %s' % type(template))
  tree = parsing.parse_str(textwrap.dedent(template))
  for k in replacements:
    replacements[k] = _convert_to_ast(replacements[k])
  results = ReplaceTransformer(replacements).visit(tree).body
  if isinstance(results, list):
    return [qual_names.resolve(r) for r in results]
  return qual_names.resolve(results)
コード例 #3
0
ファイル: qual_names_test.py プロジェクト: google/pyctr
    def test_subscript_resolve(self):
        samples = """
      x[i]
      x[i.b]
      a.b[c]
      a.b[x.y]
      a[z[c]]
      a[b[c[d]]]
      a[b].c
      a.b.c[d].e.f
      a.b[c[d]].e.f
      a.b[c[d.e.f].g].h
    """
        nodes = qual_names.resolve(parsing.parse_str(textwrap.dedent(samples)))
        nodes = tuple(n.value for n in nodes.body)

        self.assertQNStringIs(nodes[0], 'x[i]')
        self.assertQNStringIs(nodes[1], 'x[i.b]')
        self.assertQNStringIs(nodes[2], 'a.b[c]')
        self.assertQNStringIs(nodes[3], 'a.b[x.y]')
        self.assertQNStringIs(nodes[4], 'a[z[c]]')
        self.assertQNStringIs(nodes[5], 'a[b[c[d]]]')
        self.assertQNStringIs(nodes[6], 'a[b].c')
        self.assertQNStringIs(nodes[7], 'a.b.c[d].e.f')
        self.assertQNStringIs(nodes[8], 'a.b[c[d]].e.f')
        self.assertQNStringIs(nodes[9], 'a.b[c[d.e.f].g].h')
コード例 #4
0
 def test_parse_str(self):
     mod = parsing.parse_str(
         textwrap.dedent("""
         def f(x):
           return x + 1
 """))
     self.assertEqual('f', mod.body[0].name)
コード例 #5
0
ファイル: ast_util_test.py プロジェクト: google/pyctr
 def test_parallel_walk(self):
   node = parsing.parse_str(
       textwrap.dedent("""
     def f(a):
       return a + 1
   """))
   for child_a, child_b in ast_util.parallel_walk(node, node):
     self.assertEqual(child_a, child_b)
コード例 #6
0
ファイル: ast_util_test.py プロジェクト: google/pyctr
 def test_keywords_to_dict(self):
   keywords = parsing.parse_expression('f(a=b, c=1, d=\'e\')').keywords
   d = ast_util.keywords_to_dict(keywords)
   # Make sure we generate a usable dict node by attaching it to a variable and
   # compiling everything.
   node = parsing.parse_str('def f(b): pass').body[0]
   node.body.append(ast.Return(d))
   result, _ = parsing.ast_to_object(node)
   self.assertDictEqual(result.f(3), {'a': 3, 'c': 1, 'd': 'e'})
コード例 #7
0
ファイル: ast_util_test.py プロジェクト: google/pyctr
  def test_rename_symbols_attributes(self):
    node = parsing.parse_str('b.c = b.c.d')
    node = qual_names.resolve(node)

    node = ast_util.rename_symbols(
        node, {qual_names.from_str('b.c'): qual_names.QN('renamed_b_c')})

    source = parsing.ast_to_source(node)
    self.assertEqual(source.strip(), 'renamed_b_c = renamed_b_c.d')
コード例 #8
0
ファイル: ast_util_test.py プロジェクト: google/pyctr
  def test_rename_symbols_annotations(self):
    node = parsing.parse_str('a[i]')
    node = qual_names.resolve(node)
    anno.setanno(node, 'foo', 'bar')
    orig_anno = anno.getanno(node, 'foo')

    node = ast_util.rename_symbols(node,
                                   {qual_names.QN('a'): qual_names.QN('b')})

    self.assertIs(anno.getanno(node, 'foo'), orig_anno)
コード例 #9
0
ファイル: ast_util_test.py プロジェクト: google/pyctr
  def test_rename_symbols_basic(self):
    node = parsing.parse_str('a + b')
    node = qual_names.resolve(node)

    node = ast_util.rename_symbols(
        node, {qual_names.QN('a'): qual_names.QN('renamed_a')})

    self.assertIsInstance(node.body[0].value.left.id, str)
    source = parsing.ast_to_source(node)
    self.assertEqual(source.strip(), 'renamed_a + b')
コード例 #10
0
ファイル: ast_util_test.py プロジェクト: google/pyctr
 def test_apply_to_single_assignments_static_unpack(self):
   node = parsing.parse_str('a, b, c = d, e, f')
   node = node.body[0]
   ast_util.apply_to_single_assignments(node.targets, node.value,
                                        self._mock_apply_fn)
   self.assertDictEqual(self._invocation_counts, {
       ('a', 'd'): 1,
       ('b', 'e'): 1,
       ('c', 'f'): 1,
   })
コード例 #11
0
ファイル: ast_util_test.py プロジェクト: google/pyctr
 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'))
コード例 #12
0
ファイル: ast_util_test.py プロジェクト: google/pyctr
 def test_copy_clean(self):
   node = parsing.parse_str(
       textwrap.dedent("""
     def f(a):
       return a + 1
   """))
   setattr(node.body[0], '__foo', 'bar')
   new_node = ast_util.copy_clean(node)
   self.assertIsNot(new_node, node)
   self.assertIsNot(new_node.body[0], node.body[0])
   self.assertFalse(hasattr(new_node.body[0], '__foo'))
コード例 #13
0
ファイル: origin_info.py プロジェクト: google/pyctr
def create_source_map(nodes, code, filename, indices_in_code):
  """Creates a source map between an annotated AST and the code it compiles to.

  Args:
    nodes: Iterable[ast.AST, ...]
    code: Text
    filename: Optional[Text]
    indices_in_code: Union[int, Iterable[int, ...]], the positions at which
      nodes appear in code. The parsing always returns a module when parsing
      code. This argument indicates the position in that module's body at which
      the corresponding of node should appear.

  Returns:
    Dict[CodeLocation, OriginInfo], mapping locations in code to locations
    indicated by origin annotations in node.
  """
  reparsed_nodes = parsing.parse_str(code)
  reparsed_nodes = [reparsed_nodes.body[i] for i in indices_in_code]

  resolve(reparsed_nodes, code)
  result = {}

  for before, after in ast_util.parallel_walk(nodes, reparsed_nodes):
    # Note: generated code might not be mapped back to its origin.
    # TODO(mdanatg): Generated code should always be mapped to something.
    origin_info = anno.getanno(before, anno.Basic.ORIGIN, default=None)
    final_info = anno.getanno(after, anno.Basic.ORIGIN, default=None)
    if origin_info is None or final_info is None:
      continue

    line_loc = LineLocation(filename, final_info.loc.lineno)

    existing_origin = result.get(line_loc)
    if existing_origin is not None:
      # Overlaps may exist because of child nodes, but almost never to
      # different line locations. Exception make decorated functions, where
      # both lines are mapped to the same line in the AST.

      # Line overlaps: keep bottom node.
      if existing_origin.loc.line_loc == origin_info.loc.line_loc:
        if existing_origin.loc.lineno >= origin_info.loc.lineno:
          continue

      # In case of overlaps, keep the leftmost node.
      if existing_origin.loc.col_offset <= origin_info.loc.col_offset:
        continue

    result[line_loc] = origin_info

  return result
コード例 #14
0
ファイル: qual_names_test.py プロジェクト: google/pyctr
 def test_function_calls(self):
     samples = """
   a.b
   a.b()
   a().b
   z[i]
   z[i]()
   z()[i]
 """
     nodes = qual_names.resolve(parsing.parse_str(textwrap.dedent(samples)))
     nodes = tuple(n.value for n in nodes.body)
     self.assertQNStringIs(nodes[0], 'a.b')
     self.assertQNStringIs(nodes[1].func, 'a.b')
     self.assertQNStringIs(nodes[2].value.func, 'a')
     self.assertQNStringIs(nodes[3], 'z[i]')
     self.assertQNStringIs(nodes[4].func, 'z[i]')
     self.assertQNStringIs(nodes[5].value.func, 'z')
コード例 #15
0
def matches(node, pattern):
    """Basic pattern matcher for AST.

  The pattern may contain wildcards represented by the symbol '_'. A node
  matches a pattern if for every node in the tree, either there is a node of
  the same type in pattern, or a Name node with id='_'.

  Args:
    node: ast.AST
    pattern: ast.AST

  Returns:
    bool
  """
    if isinstance(pattern, str):
        pattern, = parsing.parse_str(pattern).body

    matcher = PatternMatcher(pattern)
    matcher.visit(node)
    return matcher.matches
コード例 #16
0
ファイル: qual_names_test.py プロジェクト: google/pyctr
    def test_resolve(self):
        samples = """
      a
      a.b
      (c, d.e)
      [f, (g.h.i)]
      j(k, l)
    """
        nodes = qual_names.resolve(parsing.parse_str(textwrap.dedent(samples)))
        nodes = tuple(n.value for n in nodes.body)

        self.assertQNStringIs(nodes[0], 'a')
        self.assertQNStringIs(nodes[1], 'a.b')
        self.assertQNStringIs(nodes[2].elts[0], 'c')
        self.assertQNStringIs(nodes[2].elts[1], 'd.e')
        self.assertQNStringIs(nodes[3].elts[0], 'f')
        self.assertQNStringIs(nodes[3].elts[1], 'g.h.i')
        self.assertQNStringIs(nodes[4].func, 'j')
        self.assertQNStringIs(nodes[4].args[0], 'k')
        self.assertQNStringIs(nodes[4].args[1], 'l')