azazel75/macropy

MacroPy crashes on `complex` and `bytes`

Technologicat opened this issue · 1 comments

I get a crash during macro expansion when the source AST being transformed contains complex or bytes literals. The transformed AST contains the bare complex or bytes object (and hence is an invalid AST), instead of having that object inside the appropriate type of AST node.

This affects at least macroyp3 version 1.1.0b2 (latest) on PyPI, the HEAD version of MacroPy from this repo, and the HEAD version of MacroPy from my fork (which contains my PRs from 1-2 years ago).

Tested on Python 3.6. Occurs on both CPython 3.6.9 and PyPy3 7.3.0.

Test case:

# ----------------------------------------
# let.py

from macropy.core.macros import Macros
from macropy.core.quotes import macros, q, ast_literal

from ast import arg

macros = Macros()  # noqa: F811

@macros.expr
def let(tree, args, **kw):  # args; ast.Tuple: (k1, v1), (k2, v2), ..., (kn, vn)
    names = [k.id for k, _ in (a.elts for a in args)]
    if len(set(names)) < len(names):
        assert False, "binding names must be unique in the same let"
    values = [v for _, v in (a.elts for a in args)]
    lam = q[lambda: ast_literal[tree]]
    lam.args.args = [arg(arg=x) for x in names]  # inject args
    return q[ast_literal[lam](ast_literal[values])]

# ----------------------------------------
# main.py

from let import macros, let  # noqa: F401

let((x, 21))[2 * x]  # runs fine  # noqa: F821, the `let` defines `x`.
let((x, 1 + 2j))[2 * x]  # complex number, crash (invalid AST)  # noqa: F821
let((x, b"wiki"))[2 * x]  # bytestring, crash (invalid AST)  # noqa: F821

# ----------------------------------------
# wrapper.py

import macropy.activate  # noqa: F401
import main  # noqa: F401

Run with python3 wrapper.py.

Output:

jje@arcturus:~/Documents/koodit/macropy/complexcrash/$ python3 wrapper.py
Traceback (most recent call last):
  File "wrapper.py", line 2, in <module>
    import main  # noqa: F401
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 951, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 894, in _find_spec
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/import_hooks.py", line 147, in find_spec
    code, tree = self.expand_macros(source, origin, spec)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/import_hooks.py", line 111, in expand_macros
    tree, source_code, modules).expand_macros()
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 630, in expand_macros
    tree = super().expand_macros(tree)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 436, in expand_macros
    return self.walk_tree(tree)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 586, in walk_tree
    self.walk_children(tree)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 550, in walk_children
    new_value = self.walk_tree(old_value)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 586, in walk_tree
    self.walk_children(tree)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 555, in walk_children
    new_t = self.walk_tree(t)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 586, in walk_tree
    self.walk_children(tree)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 550, in walk_children
    new_value = self.walk_tree(old_value)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 577, in walk_tree
    new_tree = self.walk_tree(expand_gen.send(new_tree))
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 467, in macro_expand
    new_tree = yield expand_single_gen.send(new_tree)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/macros.py", line 535, in macro_expand_single
    tuple(self.file_vars.items()))
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/cleanup.py", line 62, in fill_line_numbers
    fill_line_numbers(sub, tree.lineno, tree.col_offset)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/cleanup.py", line 56, in fill_line_numbers
    fill_line_numbers(sub, lineno, col_offset)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/cleanup.py", line 62, in fill_line_numbers
    fill_line_numbers(sub, tree.lineno, tree.col_offset)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/cleanup.py", line 62, in fill_line_numbers
    fill_line_numbers(sub, tree.lineno, tree.col_offset)
  File "/home/jje/.local/lib/python3.6/site-packages/macropy3-1.1.0b2-py3.6.egg/macropy/core/cleanup.py", line 67, in fill_line_numbers
    "after expansion".format(tree, type(tree)))
TypeError: Invalid AST node '2j',  type: '<class 'complex'>' after expansion

Hmm, the check in https://github.com/azazel75/macropy/blob/master/macropy/core/cleanup.py#L63...

Shouldn't we have bytes and complex there, too?