Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access private variables in injected method - python

Question is at the end

What am I trying to do is :

  1. Inject property to created object and set it instead of a variable (success)
  2. Inject method (let's call it METHOD) to created object, object had no such method before (success)
  3. Call public another method from property using self (success)
  4. Call METHOD from property using self (success)
  5. Get private class variable from METHOD using self (failure)

Now, here is some code:

from types import MethodType
def add_property(instance, name, method):
    cls = type(instance)
    cls = type(cls.__name__, (cls,), {})
    cls.__perinstance = True
    instance.__class__ = cls
    setattr(cls, name, property(method))

def add_variable(instance, name, init_value = 0 ):
    setattr(type(instance), name, init_value)

class Simulation:
    def __init__(self):
        self.finished = False
        self.__hidden = -10

    def someloop(self):
        while not self.finished:
            self.__private_method()

    def __private_method(self):
        pass 

    def public_method(self):
        pass

def mocked_method(self):
    print(type(self))
    print(self.__dict__)
    print(self.__hidden)

def finished(self):

    print("Execute finished",type(self))
    self.public_method()
    self.mocked_update()
    return True


simulation = Simulation() 
add_property(simulation, "finished", finished)
add_variable(simulation, "count_finished", 0)
simulation.mocked_update = MethodType(mocked_method, simulation)
simulation.someloop() 

What code produced (those prints):

Execute finished '<class '__main__.Simulation'>
<class '__main__.Simulation'>
{'finished': False, '_Simulation__hidden': -10, 'mocked_update': <bound method mocked_method of <__main__.Simulation object at 0x030D2F10>>}
(...)
AttributeError: 'Simulation' object has no attribute '__hidden'

As you can see self is what it should be (simulation class), it was properly injected and yet it doesn't work. In case you are wondering :

print(self._Simulation__hidden)

obviously works inside mocked_update.

Hence my question : Is there any chance for me to access this variable using self?

Motivation

Since there was a question in the comments section:

This does not serve any real purpose, it is just an experiment.

like image 515
MaLiN2223 Avatar asked Jan 26 '17 23:01

MaLiN2223


1 Answers

The name mangling of "private" members is strictly done within class definitions. To achieve the desired purpose, which is to have self.__hidden translated to self._Simulation_hidden you need simply define it within an appropriately named class.

For example:

def make_mocked_method():
    class Simulation:
        # this is your code, but now its inside a class stanza so '__' will be mangled
        def mocked_method(self):
            print(type(self))
            print(self.__dict__)
            print(self.__hidden)

    return Simulation.mocked_method

Now mocked_method will access the desired attribute correctly:

simulation.mocked_update = MethodType(make_mocked_method(), simulation)
simulation.someloop() 

gives:

<class '__main__.Simulation'>
{'finished': False, 'mocked_update': <bound method make_mocked_method.<locals>.Simulation.mocked_method of <__main__.Simulation object at 0x101c00a58>>, '_Simulation__hidden': -10}
-10

Extension

This relies on us hard coding the name of the class we're adding the method to (Simulation). To avoid that we can instead use an exec:

def make_mocked_method(cls):
    txt = """class {}:
        def mocked_method(self):
            print(type(self))
            print(self.__dict__)
            print(self.__hidden)
    """.format(cls.__name__)
    ns = {}
    exec(txt, ns)
    return ns[cls.__name__].mocked_method

Unfortunately here the function we wish to add must be defined as text, it can't be some already defined arbitrary function object. (That might possibly be solved by using inspect to find its source and then recompiling that source within a class stanza using exec (and judicious choice of globals).

like image 83
donkopotamus Avatar answered Sep 29 '22 03:09

donkopotamus