diff --git a/macropy/case_classes.py b/macropy/case_classes.py index 899c2a45..d4d1551b 100644 --- a/macropy/case_classes.py +++ b/macropy/case_classes.py @@ -171,6 +171,7 @@ def prep_initialization(init_fun, args, vararg, kwarg, defaults, all_args): kws.update({ 'kwonlyargs': [], 'kw_defaults': [], + 'posonlyargs': [], 'args': [ast.arg('self', None)] + [ast.arg(id, None) for id in args], 'vararg': ast.arg(vararg, None) if vararg is not None else None, @@ -235,7 +236,14 @@ def case_transform(tree, gen_sym, parents): tree.bases = parents assign = ast.FunctionDef( gen_sym("prepare_"+tree.name), - ast.arguments([], None, [], [], None, []), + ast.arguments( + posonlyargs=[], + args=[], + vararg=None, + kwonlyargs=[], + kw_defaults=[], + kwarg=None, + defaults=[]), outer, [hq[apply]], None diff --git a/macropy/core/failure.py b/macropy/core/failure.py index 6eeff151..a75df0b5 100644 --- a/macropy/core/failure.py +++ b/macropy/core/failure.py @@ -24,7 +24,7 @@ def clear_errors(tree, **kw): tb = "".join(traceback.format_tb(tree.__traceback__)) msg = str(tree) if type(tree) is not AssertionError or tree.args == (): - msg = ("".join(tree.args) + "\nCaused by Macro-Expansion Error:\n" + + msg = ("".join(map(str, tree.args)) + "\nCaused by Macro-Expansion Error:\n" + tb) return hq[raise_error(MacroExpansionError(msg))] else: diff --git a/macropy/core/hquotes.py b/macropy/core/hquotes.py index bd215baf..e053d366 100644 --- a/macropy/core/hquotes.py +++ b/macropy/core/hquotes.py @@ -132,8 +132,16 @@ def hygienator(tree, stop, scope, **kw): id, subtree = res if 'unhygienic' == id: stop() - tree.slice.value.ctx = None - return tree.slice.value + if isinstance(tree.slice, ast.Index): + # Python 3.8- + tree.slice.value.ctx = None + return tree.slice.value + elif isinstance(tree.slice, ast.expr): + # Python 3.9+ + tree.slice.ctx = None + return tree.slice + else: + assert False, f'Wrong type: {type(tree.slice)}' macros.expose_unhygienic(ast) diff --git a/macropy/core/macros.py b/macropy/core/macros.py index 4127ead8..f0cd7ee8 100644 --- a/macropy/core/macros.py +++ b/macropy/core/macros.py @@ -105,11 +105,18 @@ class Expr(MacroType): brackets, like ``amacro[foo]``.""" def detect_macro(self, in_tree): - if (isinstance(in_tree, ast.Subscript) and - type(in_tree.slice) is ast.Index): # noqa: E129 - body_tree = in_tree.slice.value + if isinstance(in_tree, ast.Subscript): name, macro_tree, call_args = self.get_macro_details(in_tree.value) if name is not None and name in self.registry: + if type(in_tree.slice) is ast.Index: + # Python 3.8- + body_tree = in_tree.slice.value + elif isinstance(in_tree.slice, ast.expr): + # Python 3.8+ + body_tree = in_tree.slice + else: + assert False, f'Wrong type: {type(in_tree.slice)}' + new_tree = yield MacroData(self.registry[name], macro_tree, body_tree, call_args, {}, name) assert isinstance(new_tree, ast.expr), ('Wrong type %r' % @@ -117,6 +124,7 @@ def detect_macro(self, in_tree): new_tree = ast.Expr(new_tree) + class Block(MacroType): """Handles block macros, defined by using a ``with`` statement, like: @@ -614,8 +622,12 @@ def detect_macros(tree, from_fullname, from_package=None, from_module=None): if name.name not in mod.macros.decorator.registry ] + # lineno and col_offset are required fields, so we set them to 0. + # If this causes issues we might consider setting them to the + # lineno following the last actual import, but this too might cause + # issues, since it might collide with existing code. stmt.names.extend([ - ast.alias(x, x) for x in + ast.alias(x, x, lineno=0, col_offset=0) for x in mod.macros.expose_unhygienic.registry.keys() ]) @@ -624,6 +636,11 @@ def detect_macros(tree, from_fullname, from_package=None, from_module=None): def check_annotated(tree): """Shorthand for checking if an AST is of the form something[...].""" - if (isinstance(tree, ast.Subscript) and type(tree.slice) is ast.Index and - type(tree.value) is ast.Name): # noqa: E129 + if not isinstance(tree, ast.Subscript) or type(tree.value) is not ast.Name: + return None + if type(tree.slice) is ast.Index: + # Python 3.8- return tree.value.id, tree.slice.value + elif isinstance(tree.slice, ast.expr): + # Python 3.9+ + return tree.value.id, tree.slice