Injecting new names into the above frame

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Peter Waller

    Injecting new names into the above frame

    Dear Pythoners,

    I know this will probably be perceived as 'evil voodoo', and fair
    enough: it probably is. I guess it is unpythonic.

    ... but I want to know how to do it anyway - mostly for my own
    interest.

    Consider the following snippet of code:

    ---
    def Get( *names ):
    if not names: return None

    frame = sys._getframe(1 )
    prevFrameLocals = frame.f_locals

    for name in names:
    prevFrameLocals[ name ] = FetchObjectName d( name )

    Get("a", "b", "c")

    print a, b, c
    ---

    FetchObjectName d() is an arbitrary function which takes a string and
    returns an object it got from some store somewhere.

    This works fine at the module level, because names in the locals/
    globals dictionary can be played with in this way. The idea is to save
    lots of typing, i.e.

    a, b, c = Get("a","b","c" )

    ...gets frustrating after much typing for many objects with long names.
    This is just an example, there are other instances I have where it
    would be nice to inject names into the frame above.

    Of course, we hit a road block when we call 'Get' from a function
    rather than a module, because the locals dictionary does not get
    copied back into the code object automatically, so we have to add this
    snippet before the Get() function returns:

    from ctypes import pythonapi, py_object, c_int
    pythonapi.PyFra me_LocalsToFast ( py_object( frame ), 1 )

    This copies back the names into the code object, and works fine.. that
    is, if the names already exist within the code object.

    def MyFunction():
    a = None
    Get("a")
    print a # Works
    Get("b")
    print b # Name error, b is undefined

    Is there any way for Get() to define a new variable within
    MyFunction's code object? Or is there any programmatic way to, at
    runtime, insert new names into functions?

    I don't care how hacky it is and whether it requires making calls to
    python's internals with ctypes - maybe the whole code object needs to
    be replaced? is it even possible to do that when the Get() function is
    about to return to this new code object?

    Cheers,

    - Peter
  • Carsten Haese

    #2
    Re: Injecting new names into the above frame

    Peter Waller wrote:
    Dear Pythoners,
    >
    I know this will probably be perceived as 'evil voodoo', and fair
    enough: it probably is. I guess it is unpythonic.
    >
    .. but I want to know how to do it anyway - mostly for my own
    interest.
    Well, if you're really just asking out of curiosity, it should be
    sufficient to tell you that this is not possible.
    [...] we have to add this
    snippet before the Get() function returns:
    >
    from ctypes import pythonapi, py_object, c_int
    pythonapi.PyFra me_LocalsToFast ( py_object( frame ), 1 )
    >
    This copies back the names into the code object, and works fine.. that
    is, if the names already exist within the code object.
    >
    def MyFunction():
    a = None
    Get("a")
    print a # Works
    Get("b")
    print b # Name error, b is undefined
    The answer to why this doesn't work lies in the disassembly of that
    function:
    0 LOAD_CONST 0 (0)
    3 STORE_FAST 0 (0)
    6 LOAD_GLOBAL 1 (1)
    9 LOAD_CONST 1 (1)
    12 CALL_FUNCTION 1
    15 POP_TOP
    16 LOAD_FAST 0 (0) <- This is a
    19 PRINT_ITEM
    20 PRINT_NEWLINE
    21 LOAD_GLOBAL 1 (1)
    24 LOAD_CONST 2 (2)
    27 CALL_FUNCTION 1
    30 POP_TOP
    31 LOAD_GLOBAL 2 (2) <- This is b
    34 PRINT_ITEM
    35 PRINT_NEWLINE
    36 LOAD_CONST 0 (0)
    39 RETURN_VALUE

    Since you have an assignment to the name a, a is recognized as a local
    name at compile time. b is not recognized as a local name at compile
    time, so even if you inject a value for b into the locals dictionary,
    the byte code still looks up the value as a global name.
    Is there any way for Get() to define a new variable within
    MyFunction's code object? Or is there any programmatic way to, at
    runtime, insert new names into functions?
    Not without making fundamental changes to Python itself. The fact that
    this isn't possible is a feature, in my opinion. I like the fact that I
    can call a function and be *absolutely certain* that it's not going to
    pollute my local namespace.

    Hope this helps,

    --
    Carsten Haese

    Comment

    • Peter Waller

      #3
      Re: Injecting new names into the above frame

      On Sep 12, 2:30 pm, Carsten Haese <carsten.ha...@ gmail.comwrote:
      >
      The answer to why this doesn't work lies in the disassembly of that
      function:
      This makes me want to ask: is it difficult to modify a function's
      code? Even if it weren't possible whilst the function was executing
      (because then I would be returning to a function I just created, I
      guess?) Could I make another function which converted this LOAD_GLOBAL
      to LOAD_FAST?

      I agree that in general a function should have no side effects and I
      would like to know exactly what it is going to do, and that what I'm
      doing is unpythonic.

      My curiosity extends because I would like to get to know a bit more
      about python's internals. My gut tells me there has to be a way to
      achieve what I'm after - even if it the result is extremely ugly.
      Maybe it might involve applying a decorator to functions who use this
      functionality, who change the code so that these calls work?

      Cheers for your reply,

      - Pete

      Comment

      • Michele Simionato

        #4
        Re: Injecting new names into the above frame

        On Sep 13, 1:35 pm, Peter Waller <peter.wal...@g mail.comwrote:
        This makes me want to ask: is it difficult to modify a function's
        code?
        No, it is not difficult. Look at the byteplay module:
        it makes possible all kinds of dirty hacks.

        Comment

        • Boris Borcic

          #5
          Re: Injecting new names into the above frame

          Why don't you use import and __import__() ? - They seem designed for such an
          application.

          I mean, I am not against vicious hacks for the fun of them, but not if they
          serve the illusion that what they do can't (easily) be achieved other ways.

          Cheers, BB

          Peter Waller wrote:
          Dear Pythoners,
          >
          I know this will probably be perceived as 'evil voodoo', and fair
          enough: it probably is. I guess it is unpythonic.
          >
          .. but I want to know how to do it anyway - mostly for my own
          interest.
          >
          Consider the following snippet of code:
          >
          ---
          def Get( *names ):
          if not names: return None
          >
          frame = sys._getframe(1 )
          prevFrameLocals = frame.f_locals
          >
          for name in names:
          prevFrameLocals[ name ] = FetchObjectName d( name )
          >
          Get("a", "b", "c")
          >
          print a, b, c
          ---
          >
          FetchObjectName d() is an arbitrary function which takes a string and
          returns an object it got from some store somewhere.
          >
          This works fine at the module level, because names in the locals/
          globals dictionary can be played with in this way. The idea is to save
          lots of typing, i.e.
          >
          a, b, c = Get("a","b","c" )
          >
          ..gets frustrating after much typing for many objects with long names.
          This is just an example, there are other instances I have where it
          would be nice to inject names into the frame above.
          >
          Of course, we hit a road block when we call 'Get' from a function
          rather than a module, because the locals dictionary does not get
          copied back into the code object automatically, so we have to add this
          snippet before the Get() function returns:
          >
          from ctypes import pythonapi, py_object, c_int
          pythonapi.PyFra me_LocalsToFast ( py_object( frame ), 1 )
          >
          This copies back the names into the code object, and works fine.. that
          is, if the names already exist within the code object.
          >
          def MyFunction():
          a = None
          Get("a")
          print a # Works
          Get("b")
          print b # Name error, b is undefined
          >
          Is there any way for Get() to define a new variable within
          MyFunction's code object? Or is there any programmatic way to, at
          runtime, insert new names into functions?
          >
          I don't care how hacky it is and whether it requires making calls to
          python's internals with ctypes - maybe the whole code object needs to
          be replaced? is it even possible to do that when the Get() function is
          about to return to this new code object?
          >
          Cheers,
          >
          - Peter
          --

          >

          Comment

          Working...