Adding Macros to Python

前端 未结 7 977
无人及你
无人及你 2021-02-02 07:41

I would like to invoke the following code in-situ wherever I refer to MY_MACRO in my code below.

# MY_MACRO
frameinfo = getframeinf         


        
相关标签:
7条回答
  • 2021-02-02 08:16

    If you need only line and function name of caller like I needed for debug, you can get caller function information by inspect.getouterframes link.

    import inspect
    def printDebugInfo():
      (frame,filename,line_number,function_name, lines, 
        index) = inspect.getouterframes(inspect.currentframe())[1]
      print(filename, line_number, function_name, lines, index)
    
    def f1():
      printDebugInfo()
    
    if __name__=='__main__':
      f1()
    
    0 讨论(0)
  • 2021-02-02 08:20

    I'm not sure if this is a good solution, but it's at least worth considering a macro preprocessor.

    There are a few different extend-Python-with-macros projects, or wider projects that should make such a thing easier to do, but I only have expired links for all of them (Logix, MetaPython, Mython, Espy)… It might be worth looking for current links and/or newer/liver projects.

    You can use something like m4 or cpp, or something more powerful, or even build one yourself. But really, you've just got a small, static set (so far, one) of purely textual macros. At worst you have to detect the indentation level of MY_MACRO and add that to the start of each line, which is trivial to do in a regex. Meaning sed, or a 3-liner Python script, can be your preprocessor.

    However, there are two problems, or at least annoyances.

    First, you need to preprocess your files. If you're already using C extension modules or generated code or any other code that needs you to setup.py (or make or scons or whatever) before you can run it, or you're using an IDE where you just hit cmd-R or ctrl-shift-B or whatever to test your code, this isn't a problem. But for the typical edit-test loop with a text editor in one window and an interactive interpreter in another… well, you've just turned it into an edit-compile-test loop. Ugh. The only solution I can think of is an import hook that preprocesses every file before importing it as a module, which seems like a lot of work for a small benefit.

    Second, your line numbers and source (from MY_MACRO itself, as well as from tracebacks and inspect.getsource and so on) are going to be the line numbers of the preprocessed files, not the original source that you have open for editing. Since your preprocessed files are pretty readable, that isn't terrible (not as bad as coding CoffeeScript and debugging it as JavaScript, which most of the CoffeeScript community does every day…), but it's definitely an annoyance.

    Of course one way to solve this is to build your own macro processor into the interpreter, at whichever stage in the parse/compile process you want. I'm guessing that's a whole lot more work than you want to do, but if you do, well, Guido always prefers to have an actual working design and implementation to reject instead of having to keep rejecting vague suggestions of "Hey, let's add macros to Python". :)

    0 讨论(0)
  • 2021-02-02 08:25

    MacroPy is a project of mine which brings syntactic macros to Python. The project is only 3 weeks old, but if you look at the link, you'll see we have a pretty cool collection of demos, and the functionality you want can definitely be implemented using it.

    On the other hand, python has some pretty amazing introspection capabilities, so I suspect you may be able to accomplish what you want purely using that functionality.

    0 讨论(0)
  • 2021-02-02 08:25

    How about a function you can call? This function accesses the caller's frame, and rather than using locals(), uses frame.f_locals to get the caller's namespace.

    def my_function():
        frame = currentframe().f_back
        msg = 'We are on file {0.f_code.co_filename} and line {0.f_lineno}'.format(frame)
        current_state = frame.f_locals
        print current_state['some_variable']
    

    Then just call it:

    def some_function:
        my_function()
    
    def some_other_function:
        some_function()
        my_function()
    
    class some_class:
      def some_method:
         my_function()
    
    0 讨论(0)
  • 2021-02-02 08:29

    The use of exec is frowned upon, but it ought to do the trick here. For example, take the following macro:

    MY_MACRO = """
    print foo            
    """
    

    and run it by using the following code:

    foo = "breaking the rules"
    exec MY_MACRO in globals(),locals() 
    

    Always be careful with exec, because it can have strange side-effects and opens up opportunities for code injection.

    0 讨论(0)
  • 2021-02-02 08:29

    you could use function if you wanted to:

    def MY_MACRO():
        frame = currentframe()
        try:
            macro_caller_locals = frame.f_back.f_locals
            print(macro_caller_locals['a'])
    
        finally:
            del frame
    
    def some_function:
        a = 1
        MY_MACRO()
    
    0 讨论(0)
提交回复
热议问题