Repository: serge-sans-paille/gast

Issue:
ast.Assign need type_comment in Python3.8
In python3.8.5，I use gast to modify ast_node then convert back into ast by`gast_to_ast`. But the result is different with original `ast`.

It works in Python3.5 and Python2.7

The example code:

```python
import ast
import gast
import textwrap
import unittest

def code_gast_ast(source):
    """
    Transform source_code into gast.Node and modify it,
    then back to ast.Node.
    """
    source = textwrap.dedent(source)
    root = gast.parse(source)
    new_root = GastNodeTransformer(root).apply()
    ast_root = gast.gast_to_ast(new_root)
    return ast.dump(ast_root)


def code_ast(source):
    """
    Transform source_code into ast.Node, then dump it.
    """
    source = textwrap.dedent(source)
    root = ast.parse(source)
    return ast.dump(root)


class GastNodeTransformer(gast.NodeTransformer):
    def __init__(self, root):
        self.root = root

    def apply(self):
        return self.generic_visit(self.root)

    def visit_Name(self, node):
        """
        Param in func is ast.Name in PY2, but ast.arg in PY3.
        It will be generally represented by gast.Name in gast.
        """
        if isinstance(node.ctx, gast.Param) and node.id != "self":
            node.id += '_new'

        return node

class TestPythonCompatibility(unittest.TestCase):
    def _check_compatibility(self, source, target):
        source_dump = code_gast_ast(source)
        target_dump = code_ast(target)
        self.assertEqual(source_dump, target_dump)

    def test_call(self):
        source = """
            y = foo(*arg)
        """
        target = """
            y = foo(*arg_new)
        """
        self._check_compatibility(source, target)

# source_dump  gast-> ast
# Module(body=[Assign(targets=[Name(id='y', ctx=Store())], value=Call(func=Name(id='foo', ctx=Load()), args=[Starred(value=Name(id='arg_new', ctx=Load()), ctx=Load())], keywords=[]))], type_ignores=[])

# target_dump ast
# Module(body=[Assign(targets=[Name(id='y', ctx=Store())], value=Call(func=Name(id='foo', ctx=Load()), args=[Starred(value=Name(id='arg_new', ctx=Load()), ctx=Load())], keywords=[]), type_comment=None)], type_ignores=[])
```

After I modified the defination in `gast.py`, it works in python3.8

from
```
('Assign', (('targets', 'value',),
                ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
                (stmt,))),
```
into
```
('Assign', (('targets', 'value','type_comment'),
                ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
                (stmt,))),
```

Discussion:

Patch:
diff --git a/gast/ast2.py b/gast/ast2.py
index 0e0a8ed..8968b5b 100644
--- a/gast/ast2.py
+++ b/gast/ast2.py
@@ -40,6 +40,17 @@ class Ast2ToGAst(AstToGAst):
         new_node.end_lineno = new_node.end_col_offset = None
         return new_node
 
+    def visit_Assign(self, node):
+        new_node = gast.Assign(
+            self._visit(node.targets),
+            self._visit(node.value),
+            None,  # type_comment
+        )
+
+        gast.copy_location(new_node, node)
+        new_node.end_lineno = new_node.end_col_offset = None
+        return new_node
+
     def visit_For(self, node):
         new_node = gast.For(
             self._visit(node.target),
@@ -278,6 +289,15 @@ class GAstToAst2(GAstToAst):
         ast.copy_location(new_node, node)
         return new_node
 
+    def visit_Assign(self, node):
+        new_node = ast.Assign(
+            self._visit(node.targets),
+            self._visit(node.value),
+        )
+
+        ast.copy_location(new_node, node)
+        return new_node
+
     def visit_For(self, node):
         new_node = ast.For(
             self._visit(node.target),
diff --git a/gast/ast3.py b/gast/ast3.py
index 2d56fca..d0f0d39 100644
--- a/gast/ast3.py
+++ b/gast/ast3.py
@@ -15,6 +15,17 @@ class Ast3ToGAst(AstToGAst):
         def visit_Index(self, node):
             return self._visit(node.value)
 
+        def visit_Assign(self, node):
+            new_node = gast.Assign(
+                self._visit(node.targets),
+                self._visit(node.value),
+                None,  # type_comment
+            )
+
+            gast.copy_location(new_node, node)
+            new_node.end_lineno = new_node.end_col_offset = None
+            return new_node
+
     if sys.version_info.minor < 8:
         def visit_Module(self, node):
             new_node = gast.Module(
@@ -247,6 +258,15 @@ class GAstToAst3(GAstToAst):
             ast.copy_location(new_node, node)
             return new_node
 
+    def visit_Assign(self, node):
+        new_node = ast.Assign(
+            self._visit(node.targets),
+            self._visit(node.value),
+        )
+
+        ast.copy_location(new_node, node)
+        return new_node
+
     if sys.version_info.minor < 8:
 
         def visit_Module(self, node):
diff --git a/gast/gast.py b/gast/gast.py
index 317d4b1..00150ca 100644
--- a/gast/gast.py
+++ b/gast/gast.py
@@ -61,7 +61,7 @@ _nodes = (
     ('Delete', (('targets',),
                 ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
                 (stmt,))),
-    ('Assign', (('targets', 'value',),
+    ('Assign', (('targets', 'value', 'type_comment'),
                 ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
                 (stmt,))),
     ('AugAssign', (('target', 'op', 'value',),


Test patch:
diff --git a/tests/test_compat.py b/tests/test_compat.py
index e944647..c04c433 100644
--- a/tests/test_compat.py
+++ b/tests/test_compat.py
@@ -61,7 +61,8 @@ class CompatTestCase(unittest.TestCase):
                 compile(gast.gast_to_ast(tree), '<test>', 'exec')
                 norm = ("Module(body=[Assign(targets=[Name(id='e', ctx=Store()"
                         ", annotation=None, type_comment=None"
-                        ")], value=Constant(value=1, kind=None)), Expr(value="
+                        ")], value=Constant(value=1, kind=None), "
+                        "type_comment=None), Expr(value="
                         "JoinedStr(values=[FormattedValue(value=Name(id='e', "
                         "ctx=Load(), annotation=None, type_comment=None), "
                         "conversion=-1, format_spec=None)]))], "
@@ -74,7 +75,8 @@ class CompatTestCase(unittest.TestCase):
                 compile(gast.gast_to_ast(tree), '<test>', 'exec')
                 norm = ("Module(body=[Assign(targets=[Name(id='e', ctx=Store()"
                         ", annotation=None, type_comment=None"
-                        ")], value=Constant(value=1, kind=None)), Expr(value="
+                        ")], value=Constant(value=1, kind=None), "
+                        "type_comment=None), Expr(value="
                         "JoinedStr(values=[Constant(value='e = ', kind=None), "
                         "FormattedValue(value=Name(id='e', ctx=Load(), "
                         "annotation=None, type_comment=None), "