Skip to content

Remove restriction on macros in __main__ #92

@gilch

Description

@gilch

https://macropy3.readthedocs.io/en/latest/overview.html says,

Note that this means you cannot use macros in a file that is run directly, as it will not be passed through the import hooks.

Sometimes I use Python for big projects, but sometimes I just need a one-page script and just want to import my macros and run it directly. It's a pain to have to write a new launcher to import it every time just so I can have macros.

Writing a macropy launcher aliased to replace python altogether would help some, but it messes up the if __name__ == '__main__': pattern I need to use sometimes. Then I'd have to remember to launch with normal python instead, but only if I didn't use macros...

Anyway, I think we can do better.


macros/__init__.py

import macropy.activate
from . import macros_in_main

macros/macros_in_main.py

import ast, importlib, inspect

from macropy.core.macros import ModuleExpansionContext, detect_macros

frame = inspect.currentframe()
while frame.f_globals["__name__"] != "__main__":
    frame = frame.f_back

source = inspect.getsource(frame)

tree = ast.parse(source)
exec(
    compile(
        ast.Module(
            ModuleExpansionContext(
                tree,
                source,
                [
                    (importlib.import_module(mod), bind)
                    for mod, bind in (detect_macros(tree, "__main__"))
                ],
            )
            .expand_macros()
            .body
        ),
        "__main__",
        "exec",
    )
)
raise SystemExit

main.py

import macros

from macropy.case_classes import macros, enum

@enum
class Direction:
    North, South, East, West

print(Direction(name="North")) # Direction.North

$ python main.py
Direction.North

This is just a proof of concept. I'm probably missing some edge cases and interactions, but it proves that it's at least possible to have syntactic macros, even in a file that is run "directly".

Maybe there's an easier way to do this, but I didn't see any kind of exec_with_macros() in the library.

Something like the above could perhaps replace the import macropy.activate to allow a script to be both imported and run on its own (the usual use case for the if __name__ == '__main__': construct).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions