The previous break-in in #3 was petty and small; you'd have to write the smelly code every time you wanted to pickpocket. This time, however, the break-in is orchestrated by a burglar that intends to leave a ladder at the scene of the crime, in order to allow others to follow in their footsteps. After the burglar sets up their tools, anybody can call a public method on an instance of the target class and walk away with the cheese.
First, we need a module which I'll call box, which holds our objective:
from lang.access import enforce_private
@enforce_private
class Box(object):
def __init__(self):
self._cheese_ = 42
And now the burglar shows up with unbox, which must be slightly tailored to the objective. In particular, we need to know which class we are breaking into, and which attributes we plan to steal.
# inspect: Can be used for evil.
import inspect
import sys
import types
# Splice into the security system. We build a fake module and give it
# inspect's attributes.
ourInspect = types.ModuleType("inspect")
for k, v in vars(inspect).items():
setattr(ourInspect, k, v)
# Patch out the troublesome routine.
def getsourcelines(specimen):
print "Cutting power to the security system"
return [], 0
ourInspect.getsourcelines = getsourcelines
# And reassemble the casing.
sys.modules["inspect"] = ourInspect
# Our ladder does not have to be constructed here, but it seemed neater.
def ladder(self):
return self._cheese_
And now the night of the heist. We import our unbox module first, which prepares global state for the ladder, and then we install our ladder and leave it up for anybody to use:
>>> import unbox
>>> import box
>>> vars(box.Box) # NB: The hook isn't there until at least one instance of the class has been made!
dict_proxy({'__module__': 'lang.access', '__doc__': None, '__init__': <function __init__ at 0x7fd5837909b0>})
>>> b = box.Box()
Cutting power to the security system
Cutting power to the security system
>>> vars(box.Box) # Ah, there they are.
dict_proxy({'__module__': 'lang.access', '__getattribute__': <function <lambda> at 0x7fd5837908c0>, '__doc__': None, '__init__': <function __init__ at 0x7fd5837909b0>, '__setattr__': <function <lambda> at 0x7fd583790a28>})
>>> box.Box.__getattribute__.im_func.func_closure[0].cell_contents.ladder = unbox.ladder
>>> b.ladder() # Isn't Python fun?
Cutting power to the security system
Cutting power to the security system
42
>>> box.Box().ladder() # And now all Boxes have the ladder installed.
Cutting power to the security system
Cutting power to the security system
Cutting power to the security system
Cutting power to the security system
42
We do trash the inspect module globally here, so if you wanted to use inspect in the rest of your application, then that could be a problem. Additionally, obviously lang.access could try to get rid of the patched inspect module with something like del sys.modules["inspect"]; import inspect. However, both of these concerns could be fixed in favor of the burglar by importing the box module with a PEP 302 import hook. Exocet is my preferred tool for this, but it's not hard to write your own.
Edit: Add some links.
The previous break-in in #3 was petty and small; you'd have to write the smelly code every time you wanted to pickpocket. This time, however, the break-in is orchestrated by a burglar that intends to leave a ladder at the scene of the crime, in order to allow others to follow in their footsteps. After the burglar sets up their tools, anybody can call a public method on an instance of the target class and walk away with the cheese.
First, we need a module which I'll call
box, which holds our objective:And now the burglar shows up with
unbox, which must be slightly tailored to the objective. In particular, we need to know which class we are breaking into, and which attributes we plan to steal.And now the night of the heist. We import our
unboxmodule first, which prepares global state for the ladder, and then we install our ladder and leave it up for anybody to use:We do trash the
inspectmodule globally here, so if you wanted to useinspectin the rest of your application, then that could be a problem. Additionally, obviouslylang.accesscould try to get rid of the patchedinspectmodule with something likedel sys.modules["inspect"]; import inspect. However, both of these concerns could be fixed in favor of the burglar by importing theboxmodule with a PEP 302 import hook. Exocet is my preferred tool for this, but it's not hard to write your own.Edit: Add some links.