From 86b98a11559da7d1b21dc9b4c6b10511b9095bc4 Mon Sep 17 00:00:00 2001
From: Simon Cross <hodgestar@gmail.com>
Date: Sun, 16 Feb 2014 18:46:15 +0000
Subject: [PATCH 05/16] Add support for Python 3.4 AST (support for
 NameConstants and changes to existing to arguments node attributes).

---
 genshi/template/astutil.py | 31 ++++++++++++++++++++++++++++---
 genshi/template/eval.py    | 34 +++++++++++++++++++---------------
 2 files changed, 47 insertions(+), 18 deletions(-)

diff --git a/genshi/template/astutil.py b/genshi/template/astutil.py
index a4c21c8..a3946b4 100644
--- a/genshi/template/astutil.py
+++ b/genshi/template/astutil.py
@@ -21,7 +21,7 @@ else:
     def parse(source, mode):
         return compile(source, '', mode, _ast.PyCF_ONLY_AST)
 
-from genshi.compat import IS_PYTHON2
+from genshi.compat import IS_PYTHON2, isstring
 
 __docformat__ = 'restructuredtext en'
 
@@ -103,8 +103,13 @@ class ASTCodeGenerator(object):
         self._new_line()
         return self.visit(node.body)
 
+    # Python < 3.4
     # arguments = (expr* args, identifier? vararg,
     #              identifier? kwarg, expr* defaults)
+    #
+    # Python >= 3.4
+    # arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults,
+    #              arg? kwarg, expr* defaults)
     def visit_arguments(self, node):
         first = True
         no_default_count = len(node.args) - len(node.defaults)
@@ -122,13 +127,21 @@ class ASTCodeGenerator(object):
                 self._write(', ')
             else:
                 first = False
-            self._write('*' + node.vararg)
+            self._write('*')
+            if isstring(node.vararg):
+                self._write(node.vararg)
+            else:
+                self.visit(node.vararg)
         if getattr(node, 'kwarg', None):
             if not first:
                 self._write(', ')
             else:
                 first = False
-            self._write('**' + node.kwarg)
+            self._write('**')
+            if isstring(node.kwarg):
+                self._write(node.kwarg)
+            else:
+                self.visit(node.kwarg)
 
     if not IS_PYTHON2:
         # In Python 3 arguments get a special node
@@ -724,6 +737,17 @@ class ASTCodeGenerator(object):
     def visit_Name(self, node):
         self._write(node.id)
 
+    # NameConstant(singleton value)
+    def visit_NameConstant(self, node):
+        if node.value is None:
+            self._write('None')
+        elif node.value is True:
+            self._write('True')
+        elif node.value is False:
+            self._write('False')
+        else:
+            raise Exception("Unknown NameConstant %r" % (node.value,))
+
     # List(expr* elts, expr_context ctx)
     def visit_List(self, node):
         self._write('[')
@@ -829,6 +853,7 @@ class ASTTransformer(object):
     visit_Attribute = _clone
     visit_Subscript = _clone
     visit_Name = _clone
+    visit_NameConstant = _clone
     visit_List = _clone
     visit_Tuple = _clone
 
diff --git a/genshi/template/eval.py b/genshi/template/eval.py
index 89aec49..de4bc86 100644
--- a/genshi/template/eval.py
+++ b/genshi/template/eval.py
@@ -24,7 +24,8 @@ from genshi.template.astutil import ASTTransformer, ASTCodeGenerator, \
 from genshi.template.base import TemplateRuntimeError
 from genshi.util import flatten
 
-from genshi.compat import get_code_params, build_code_chunk, IS_PYTHON2
+from genshi.compat import get_code_params, build_code_chunk, isstring, \
+                          IS_PYTHON2
 
 __all__ = ['Code', 'Expression', 'Suite', 'LenientLookup', 'StrictLookup',
            'Undefined', 'UndefinedError']
@@ -495,28 +496,31 @@ class TemplateASTTransformer(ASTTransformer):
     def __init__(self):
         self.locals = [CONSTANTS]
 
+    def _process(self, names, node):
+        if not IS_PYTHON2 and isinstance(node, _ast.arg):
+            names.add(node.arg)
+        elif isstring(node):
+            names.add(node)
+        elif isinstance(node, _ast.Name):
+            names.add(node.id)
+        elif isinstance(node, _ast.alias):
+            names.add(node.asname or node.name)
+        elif isinstance(node, _ast.Tuple):
+            for elt in node.elts:
+                self._process(names, elt)
+
     def _extract_names(self, node):
         names = set()
-        def _process(node):
-            if not IS_PYTHON2 and isinstance(node, _ast.arg):
-                names.add(node.arg)
-            if isinstance(node, _ast.Name):
-                names.add(node.id)
-            elif isinstance(node, _ast.alias):
-                names.add(node.asname or node.name)
-            elif isinstance(node, _ast.Tuple):
-                for elt in node.elts:
-                    _process(elt)
         if hasattr(node, 'args'):
             for arg in node.args:
-                _process(arg)
+                self._process(names, arg)
             if hasattr(node, 'vararg'):
-                names.add(node.vararg)
+                self._process(names, node.vararg)
             if hasattr(node, 'kwarg'):
-                names.add(node.kwarg)
+                self._process(names, node.kwarg)
         elif hasattr(node, 'names'):
             for elt in node.names:
-                _process(elt)
+                self._process(names, elt)
         return names
 
     def visit_Str(self, node):
-- 
2.12.0

