Repository: neogeny/TatSu

Issue:
tatsu.ast.AST objects don't pickle/unpickle properly
Observe:

```
>>> from tatsu.ast import AST
>>> a=AST(parseinfo=('Some parseinfo'))
>>> a
{'parseinfo': 'Some parseinfo'}
>>> import pickle
>>> pickle.loads(pickle.dumps(a))
{'parseinfo_': 'Some parseinfo'}
```

Note that after pickling/unpickling, the AST object is different.

I believe that this can be fixed by changing `__reduce__` to:

```
    def __reduce__(self):
        return (AST, (list(self.items()),))
```

Discussion:

Patch:
diff --git a/tatsu/ast.py b/tatsu/ast.py
index 3d624c3..c67873e 100644
--- a/tatsu/ast.py
+++ b/tatsu/ast.py
@@ -98,7 +98,7 @@ class AST(dict):
             return True
 
     def __reduce__(self):
-        return (AST, (), None, None, iter(self.items()))
+        return (AST, (list(self.items()),))
 
     def _safekey(self, key):
         while self.__hasattribute__(key):


Test patch:
diff --git a/test/ast_test.py b/test/ast_test.py
index 0f39cef..de472e9 100644
--- a/test/ast_test.py
+++ b/test/ast_test.py
@@ -1,9 +1,16 @@
 # -*- coding: utf-8 -*-
 import unittest
+import pickle
 
 from tatsu.ast import AST
 
 
+def test_ast_pickling():
+    a = AST(parseinfo=('Some parseinfo'))
+    b = pickle.loads(pickle.dumps(a))
+    assert a == b
+
+
 class ASTTests(unittest.TestCase):
     def test_ast(self):
         ast = AST()
diff --git a/test/parser_equivalence_test.py b/test/parser_equivalence_test.py
index 7b552d4..7661608 100644
--- a/test/parser_equivalence_test.py
+++ b/test/parser_equivalence_test.py
@@ -133,12 +133,12 @@ def test_dynamic_compiled_ast():
     code = tatsu.to_python_sourcecode(grammar, name='Test', filename='test.py')
     module = types.ModuleType('test')
     module.__file__ = 'test.py'
-    exec(compile(code, module.__file__, 'exec'), module.__dict__)
+    exec(compile(code, module.__file__, 'exec'), module.__dict__)  # pylint: disable=exec-used
 
     dynamic_ast = parser.parse('TEST')
-    compiled_ast = module.TestParser().parse('TEST', start='test')
+    compiled_ast = module.TestParser().parse('TEST', start='test')  # pylint: disable=no-member
     assert dynamic_ast == compiled_ast
 
     dynamic_ast = parser.parse('TEST A 1')
-    compiled_ast = module.TestParser().parse('TEST A 1', start='test')
+    compiled_ast = module.TestParser().parse('TEST A 1', start='test')  # pylint: disable=no-member
     assert dynamic_ast == compiled_ast