aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/build/unreleased/296.rst7
-rw-r--r--mako/_ast_util.py187
-rw-r--r--test/test_exceptions.py16
3 files changed, 22 insertions, 188 deletions
diff --git a/doc/build/unreleased/296.rst b/doc/build/unreleased/296.rst
new file mode 100644
index 0000000..b6ba8e0
--- /dev/null
+++ b/doc/build/unreleased/296.rst
@@ -0,0 +1,7 @@
+.. change::
+ :tags: bug, py3k
+ :tickets: 296
+
+ Fixed regression where import refactors in Mako 1.0.11 caused broken
+ imports on Python 3.8.
+
diff --git a/mako/_ast_util.py b/mako/_ast_util.py
index caf5f0c..74c0851 100644
--- a/mako/_ast_util.py
+++ b/mako/_ast_util.py
@@ -8,45 +8,26 @@
ast
~~~
- The `ast` module helps Python applications to process trees of the Python
- abstract syntax grammar. The abstract syntax itself might change with
- each Python release; this module helps to find out programmatically what
- the current grammar looks like and allows modifications of it.
-
- An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as
- a flag to the `compile()` builtin function or by using the `parse()`
- function from this module. The result will be a tree of objects whose
- classes all inherit from `ast.AST`.
-
- A modified abstract syntax tree can be compiled into a Python code object
- using the built-in `compile()` function.
-
- Additionally various helper functions are provided that make working with
- the trees simpler. The main intention of the helper functions and this
- module in general is to provide an easy to use interface for libraries
- that work tightly with the python syntax (template engines for example).
-
+ This is a stripped down version of Armin Ronacher's ast module.
:copyright: Copyright 2008 by Armin Ronacher.
:license: Python License.
"""
+
+
from _ast import Add
from _ast import And
from _ast import AST
from _ast import BitAnd
from _ast import BitOr
from _ast import BitXor
-from _ast import ClassDef
from _ast import Div
from _ast import Eq
-from _ast import Expression
from _ast import FloorDiv
-from _ast import FunctionDef
from _ast import Gt
from _ast import GtE
from _ast import If
from _ast import In
-from _ast import Interactive
from _ast import Invert
from _ast import Is
from _ast import IsNot
@@ -54,8 +35,6 @@ from _ast import LShift
from _ast import Lt
from _ast import LtE
from _ast import Mod
-from _ast import mod
-from _ast import Module
from _ast import Mult
from _ast import Name
from _ast import Not
@@ -64,7 +43,6 @@ from _ast import NotIn
from _ast import Or
from _ast import PyCF_ONLY_AST
from _ast import RShift
-from _ast import Str
from _ast import Sub
from _ast import UAdd
from _ast import USub
@@ -114,106 +92,6 @@ def parse(expr, filename="<unknown>", mode="exec"):
return compile(expr, filename, mode, PyCF_ONLY_AST)
-def to_source(node, indent_with=" " * 4):
- """
- This function can convert a node tree back into python sourcecode. This
- is useful for debugging purposes, especially if you're dealing with custom
- asts not generated by python itself.
-
- It could be that the sourcecode is evaluable when the AST itself is not
- compilable / evaluable. The reason for this is that the AST contains some
- more data than regular sourcecode does, which is dropped during
- conversion.
-
- Each level of indentation is replaced with `indent_with`. Per default this
- parameter is equal to four spaces as suggested by PEP 8, but it might be
- adjusted to match the application's styleguide.
- """
- generator = SourceGenerator(indent_with)
- generator.visit(node)
- return "".join(generator.result)
-
-
-def dump(node):
- """
- A very verbose representation of the node passed. This is useful for
- debugging purposes.
- """
-
- def _format(node):
- if isinstance(node, AST):
- return "%s(%s)" % (
- node.__class__.__name__,
- ", ".join(
- "%s=%s" % (a, _format(b)) for a, b in iter_fields(node)
- ),
- )
- elif isinstance(node, list):
- return "[%s]" % ", ".join(_format(x) for x in node)
- return repr(node)
-
- if not isinstance(node, AST):
- raise TypeError("expected AST, got %r" % node.__class__.__name__)
- return _format(node)
-
-
-def copy_location(new_node, old_node):
- """
- Copy the source location hint (`lineno` and `col_offset`) from the
- old to the new node if possible and return the new one.
- """
- for attr in "lineno", "col_offset":
- if (
- attr in old_node._attributes
- and attr in new_node._attributes
- and hasattr(old_node, attr)
- ):
- setattr(new_node, attr, getattr(old_node, attr))
- return new_node
-
-
-def fix_missing_locations(node):
- """
- Some nodes require a line number and the column offset. Without that
- information the compiler will abort the compilation. Because it can be
- a dull task to add appropriate line numbers and column offsets when
- adding new nodes this function can help. It copies the line number and
- column offset of the parent node to the child nodes without this
- information.
-
- Unlike `copy_location` this works recursive and won't touch nodes that
- already have a location information.
- """
-
- def _fix(node, lineno, col_offset):
- if "lineno" in node._attributes:
- if not hasattr(node, "lineno"):
- node.lineno = lineno
- else:
- lineno = node.lineno
- if "col_offset" in node._attributes:
- if not hasattr(node, "col_offset"):
- node.col_offset = col_offset
- else:
- col_offset = node.col_offset
- for child in iter_child_nodes(node):
- _fix(child, lineno, col_offset)
-
- _fix(node, 1, 0)
- return node
-
-
-def increment_lineno(node, n=1):
- """
- Increment the line numbers of all nodes by `n` if they have line number
- attributes. This is useful to "move code" to a different location in a
- file.
- """
- for node in zip((node,), walk(node)):
- if "lineno" in node._attributes:
- node.lineno = getattr(node, "lineno", 0) + n
-
-
def iter_fields(node):
"""Iterate over all fields of a node, only yielding existing fields."""
# CPython 2.5 compat
@@ -226,65 +104,6 @@ def iter_fields(node):
pass
-def get_fields(node):
- """Like `iter_fields` but returns a dict."""
- return dict(iter_fields(node))
-
-
-def iter_child_nodes(node):
- """Iterate over all child nodes or a node."""
- for name, field in iter_fields(node):
- if isinstance(field, AST):
- yield field
- elif isinstance(field, list):
- for item in field:
- if isinstance(item, AST):
- yield item
-
-
-def get_child_nodes(node):
- """Like `iter_child_nodes` but returns a list."""
- return list(iter_child_nodes(node))
-
-
-def get_compile_mode(node):
- """
- Get the mode for `compile` of a given node. If the node is not a `mod`
- node (`Expression`, `Module` etc.) a `TypeError` is thrown.
- """
- if not isinstance(node, mod):
- raise TypeError("expected mod node, got %r" % node.__class__.__name__)
- return {Expression: "eval", Interactive: "single"}.get(
- node.__class__, "expr"
- )
-
-
-def get_docstring(node):
- """
- Return the docstring for the given node or `None` if no docstring can be
- found. If the node provided does not accept docstrings a `TypeError`
- will be raised.
- """
- if not isinstance(node, (FunctionDef, ClassDef, Module)):
- raise TypeError("%r can't have docstrings" % node.__class__.__name__)
- if node.body and isinstance(node.body[0], Str):
- return node.body[0].s
-
-
-def walk(node):
- """
- Iterate over all nodes. This is useful if you only want to modify nodes in
- place and don't care about the context or the order the nodes are returned.
- """
- from collections import deque
-
- todo = deque([node])
- while todo:
- node = todo.popleft()
- todo.extend(iter_child_nodes(node))
- yield node
-
-
class NodeVisitor(object):
"""
diff --git a/test/test_exceptions.py b/test/test_exceptions.py
index c680591..c904d65 100644
--- a/test/test_exceptions.py
+++ b/test/test_exceptions.py
@@ -195,10 +195,15 @@ ${u'привет'}
html_error = exceptions.html_error_template().render()
if compat.py3k:
assert "RuntimeError: test" in html_error.decode("utf-8")
- assert "foo = u(&quot;日本&quot;)" in html_error.decode("utf-8")
+ assert "foo = u(&quot;日本&quot;)" in html_error.decode(
+ "utf-8"
+ ) or "foo = u(&#34;日本&#34;)" in html_error.decode("utf-8")
else:
assert "RuntimeError: test" in html_error
- assert "foo = u(&quot;&#x65E5;&#x672C;&quot;)" in html_error
+ assert (
+ "foo = u(&quot;&#x65E5;&#x672C;&quot;)" in html_error
+ or "foo = u(&#34;&#x65E5;&#x672C;&#34;)" in html_error
+ )
def test_py_unicode_error_html_error_template(self):
try:
@@ -328,7 +333,7 @@ ${foobar}
def test_custom_tback(self):
try:
raise RuntimeError("error 1")
- foo("bar") # noqa
+ foo("bar") # noqa
except:
t, v, tback = sys.exc_info()
@@ -341,7 +346,10 @@ ${foobar}
# obfuscate the text so that this text
# isn't in the 'wrong' exception
- assert "".join(reversed(");touq&rab;touq&(oof")) in html_error
+ assert (
+ "".join(reversed(");touq&rab;touq&(oof")) in html_error
+ or "".join(reversed(");43#&rab;43#&(oof")) in html_error
+ )
def test_tback_no_trace_from_py_file(self):
try: