diff --git a/macropy/core/macros.py b/macropy/core/macros.py index 259356b4..8968e842 100644 --- a/macropy/core/macros.py +++ b/macropy/core/macros.py @@ -73,7 +73,7 @@ def macro_stub(func): MacroData = collections.namedtuple('MacroData', ['macro', 'macro_tree', 'body_tree', 'call_args', - 'kwargs', 'name']) + 'call_kwargs', 'extrakws', 'name']) MacroData.__doc__ = """ Contains a macro's detailed informations needed to expand it. @@ -103,22 +103,25 @@ def get_macro_details(self, macro_tree): """Given an AST tree of a macro, returns detailed informations about it. :param macro_tree: an AST tree - :returns: A tuple containing tree elements: + :returns: A tuple containing four elements: - the name of the macro; - the tree containing the macro itself (it is *macro_tree* itself as of now); - - the arguments to the macro invocation. + - arguments to the macro invocation; + - named arguments to the macro invocation. """ if isinstance(macro_tree, ast.Call): call_args = tuple(macro_tree.args) + call_kwargs = {x.arg: x.value for x in macro_tree.keywords} macro_tree = macro_tree.func else: call_args = () + call_kwargs = {} if isinstance(macro_tree, ast.Name): - return macro_tree.id, macro_tree, call_args + return macro_tree.id, macro_tree, call_args, call_kwargs else: - return None, macro_tree, call_args + return None, macro_tree, call_args, call_kwargs @abstractmethod def detect_macro(self, in_tree): @@ -147,10 +150,10 @@ 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 - name, macro_tree, call_args = self.get_macro_details(in_tree.value) + name, macro_tree, call_args, call_kwargs = self.get_macro_details(in_tree.value) if name is not None and name in self.registry: new_tree = yield MacroData(self.registry[name], macro_tree, - body_tree, call_args, {}, name) + body_tree, call_args, call_kwargs, {}, name) assert isinstance(new_tree, ast.expr), ('Wrong type %r' % type(new_tree)) new_tree = ast.Expr(new_tree) @@ -171,13 +174,12 @@ def detect_macro(self, in_tree): assert isinstance(in_tree.body, list), real_repr(in_tree.body) new_tree = None for wi in in_tree.items: - name, macro_tree, call_args = self.get_macro_details( + name, macro_tree, call_args, call_kwargs = self.get_macro_details( wi.context_expr) if name is not None and name in self.registry: new_tree = yield MacroData(self.registry[name], macro_tree, - in_tree.body, call_args, - {'target': wi.optional_vars}, - name) + in_tree.body, call_args, call_kwargs, + {'target': wi.optional_vars}, name) if new_tree: if isinstance(new_tree, ast.expr): @@ -212,7 +214,7 @@ def detect_macro(self, in_tree): additions = [] # process each decorator from the innermost outwards for dec in rev_decs: - name, macro_tree, call_args = self.get_macro_details(dec) + name, macro_tree, call_args, call_kwargs = self.get_macro_details(dec) # if the decorator is not a macro, add it to a list # for later re-insertion, either before executing an # outer macro or at the end of the loop if no macro is found @@ -226,7 +228,7 @@ def detect_macro(self, in_tree): tree.decorator_list) seen_decs = [] tree = yield MacroData(self.registry[name], macro_tree, tree, - call_args, {}, name) + call_args, call_kwargs, {}, name) if type(tree) is list: additions = tree[1:] tree = tree[0] @@ -400,7 +402,7 @@ def create_single_macro_expand_generator(self, mfunc, *args, **kwargs): def gen_macro_expand_single(tree): return self.macro_expand_single(MacroData( (mfunc, sys.modules[mfunc.__module__]), - tree, tree, args, kwargs, mfunc.__name__)) + tree, tree, args, kwargs, {}, mfunc.__name__)) return gen_macro_expand_single def expand_macro(self, mfunc, tree=None, *args, **kwargs): @@ -497,9 +499,10 @@ def macro_expand_single(self, macro_data): new_tree = mfunc( tree=new_tree, args=macro_data.call_args, + kwargs=macro_data.call_kwargs, src=self.src, expand_macros=self.expand_macros, - **dict(tuple(macro_data.kwargs.items()) + + **dict(tuple(macro_data.extrakws.items()) + tuple(self.file_vars.items())) ) # the result is a generator, treat it like a @@ -527,11 +530,12 @@ def macro_expand_single(self, macro_data): new_tree = function( tree=new_tree, args=macro_data.call_args, + kwargs=macro_data.call_kwargs, src=self.src, expand_macros=self.expand_macros, lineno=macro_data.macro_tree.lineno, col_offset=macro_data.macro_tree.col_offset, - **dict(tuple(macro_data.kwargs.items()) + + **dict(tuple(macro_data.extrakws.items()) + tuple(self.file_vars.items())) ) # yield it for one more walking diff --git a/macropy/core/test/macros/argument.py b/macropy/core/test/macros/argument.py index 3ad6ed01..38e24332 100644 --- a/macropy/core/test/macros/argument.py +++ b/macropy/core/test/macros/argument.py @@ -1,9 +1,11 @@ -from macropy.core.test.macros.argument_macros import macros, expr_macro, block_macro, decorator_macro +from macropy.core.test.macros.argument_macros import macros, expr_macro, block_macro, decorator_macro, expr_macro_with_named_args import math def run(): x = expr_macro(1 + math.sqrt(5))[10 + 10 + 10] + x = expr_macro_with_named_args(1 + 2 + 3, a=(1 + math.sqrt(5)))[10 + 10 + 10] + with block_macro(1 + math.sqrt(5)) as y: x = x + 1 @@ -11,4 +13,4 @@ def run(): def f(): pass - return x \ No newline at end of file + return x diff --git a/macropy/core/test/macros/argument_macros.py b/macropy/core/test/macros/argument_macros.py index b5b0c2e3..e60b350a 100644 --- a/macropy/core/test/macros/argument_macros.py +++ b/macropy/core/test/macros/argument_macros.py @@ -3,6 +3,13 @@ macros = macropy.core.macros.Macros() +@macros.expr +def expr_macro_with_named_args(tree, args, kwargs, **kw): + assert "a" in kwargs, kwargs + assert macropy.core.unparse(kwargs["a"]) == "(1 + math.sqrt(5))", macropy.core.unparse(kwargs["a"]) + assert list(map(macropy.core.unparse, args)) == ["((1 + 2) + 3)"], macropy.core.unparse(args) + return tree + @macros.expr def expr_macro(tree, args, **kw): assert list(map(macropy.core.unparse, args)) == ["(1 + math.sqrt(5))"], macropy.core.unparse(args)