Injecting code into a function

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Kay Schluehr

    #16
    Re: Injecting code into a function


    George Sakkis wrote:
    [color=blue]
    > That's all good, at least if I knew how to poke with bytecodes ;-)
    > What's a good starting point to look at ?[/color]

    http://sourceforge.net/projects/bytecodehacks/


    For more recent development this ANN of Christian Tismer is important:



    Ciao,
    Kay

    Comment

    • Bengt Richter

      #17
      Re: Injecting code into a function

      On 25 Apr 2005 23:31:29 -0700, "George Sakkis" <gsakkis@rutger s.edu> wrote:
      [color=blue][color=green]
      >> I'm not clear on what your real goal is, but if you just want a[/color]
      >snapshot[color=green]
      >> of what locals() is just before exiting func, that could be done with
      >> a byte-code-hacking decorator with usage looking something like
      >>
      >> #func defined before this
      >> func_locals = {}
      >> @getlocals(func , func_locals)
      >> def probefunc(): pass
      >>
      >> which would make a probefunc function that would be identical to func
      >> except that before exiting, it would do func_locals.upd ate(locals()).
      >> (you might want func_locals to be a list and do[/color]
      >func_locals.ap pend(locals())[color=green]
      >> in case func is recursive and you are interested in the all the[/color]
      >locals).
      >
      >That's all good, at least if I knew how to poke with bytecodes ;-)
      >What's a good starting point to look at ?
      >
      >By the way, the original problem was yet another property packager,
      >which I posted as recipe at the cookbook:
      >http://aspn.activestate.com/ASPN/Coo.../Recipe/410698.
      >[/color]
      I made a couple of byte code hacking decorators based on the techniques
      in Raymond Hettinger's optimization decorators, which optimize access to
      globals and fold constant expressions and I forgot what else.

      I stripped all the optimization out since I was interested in injecting
      code to preset values for selected named local variables, to avoid using
      the default argument value hack. Another variant used this plus modifying
      the call signature to do a version of currying. Also modified the line
      number map. You will have some trickier stuff to work out though, I think,
      since there may be many returns, and they can happen in try/except/finally
      context, and you'll have to see how that translates (you can see with
      disassembler), and then if you can't just overwrite some returns to jump
      to a common place, you may have to widen a place to put code and adjust
      any jumps across modified stuff.

      It would be much nicer if you can get the source and do it via AST mods.
      I am toying with the concept of a custom importer that does import-time
      "decoration " of statements or statement sequences (with their suites)

      I am thinking of using '@@' something like

      # ... statments
      @@module.decora tor(nstatements , args) # how many statements, and whatever other args
      # decorated statement(s)
      ...

      Where module has to be imported already at the time of the decorating-importer's importation
      of the above (simplest way to guarantee that it's available to execute, but not the only way).

      Of course this means that a .py file has to be available for the code you are interesting in
      modifying, and you have to catch the import. (Since there's a '@@' you will know if you don't ;-)

      Anyway, I am not going to have time for that for some time, so I'll mention what I had in mind,
      in case someone wants to do something with it:

      The decorating-importer would find the @@ lines and e.g. change

      @@decomodule.de corator(nstatem ents, <anything legal>) # how many statements, and whatever other args

      to

      __AT_AT_DECO__ = decomodule.deco rator(nstatemen tsliteral, <anything legal> ...)

      which should just be a matter of src.replace('@@ ','__AT_AT_DECO __ = ') on lines where
      line.lstrip().s tartswith('@@') is true, thus making valid python source.

      It would then call compiler.parse with the modified source for an AST to work with.
      It would walk the AST to find the __AT_AT_DECO__ assignments (saving a reference to the containing statement
      list and index of the __AT_AT_DECO__ = ... statement in the list, and extract the nstatementslite ral
      and then call the decomodule.deco rator with a slice of statements to be replaced, including the __AT_AT_DECO__
      statement, which is passed as the first statement AST node in the slice, and may have interesting things
      in the <anything legal> part, which the decorating-importer doesn't need to know about. It just needs the
      two names decomodule and decorator and the number of statements, replacing them something like

      containing_stat ement_list[index:index+int (nstatementslit eral)+1] = getattr(__impor t__(decomodule) , decorator)(
      containing_stat ement_list[index:index+int (nstatementslit eral)+1])

      IOW pass the slice of statement list nodes to the decorator and expect it to get additional info from the
      __AT_AT_DECO__ = statement that is included, and expect it to return a replacement list of AST statment nodes to
      replace what it got, which might be minor or major modifications.

      The walk would continue until all the __AT_AT_DECO__ regions were replaced, and then the whole AST would be
      compiled and made into a module delivered as if it were an ordinary import.

      Note that this is not a macro preprocessor, but depending on what is in decomodule.deco rator, one could mess with
      legal source (after src.replace('@@ ','__AT_AT_DECO __ = ') it has to satisfy compiler.parse, and later check also)
      pretty seriously, but it has to be pretty legal to start. Of course one could abuse magic-content strings, but
      metaclasses can already do a lot of that. That's not what I'm hoping to do. Just super-duper-decoration ;-)

      Anyway, that's a sketch of the idea. I won't have time for it or much else for a longish while now.
      If someone's interested, they can quote this and start another thread ;-)

      Regards,
      Bengt Richter

      Comment

      • Lonnie Princehouse

        #18
        Re: Injecting code into a function

        > I think that Your trace_returns function is actually a global trace[color=blue]
        > that returns itself and does not handle the 'call' event.[/color]

        By returning itself, the global trace also becomes the local trace.
        The code in bdb.py does the same thing--- self.trace_disp atch is set
        as the global trace, and returns self.trace_disp atch via
        self.dispatch_c all(frame, arg)

        Comment

        Working...