Is deep monkeypatching possible in Python?
Question: Take a look at this [changeset for
Django](https://github.com/django/django/commit/be0ad62994a340ad54a0b328771931932a45a899).
I need this functionality, however this patch comes from Django's 1.7 release,
which I can't use in my environment (Python 2.6 only). So for now, I've copied
the `admin_view` method to my code and injected it with a
`admin.site.admin_view = partial(admin_view, admin.site)`.

However, I would like to keep the amount of "forked" code to the minimum and
wondered: is it possible to monkeypatch it, i.e. replace the `self.login`
function with `redirect_to_login` in the execution scope of the `inner`
function of the decorator?

I'm aware that this would be an evil hack, however, I want to find out how far
one can go with Python.


Answer: `django.contrib.admin.sites.inner = yourfunctionhere`

EDIT:

Wow. Kind of embarrassed I let this sit here for so long. I vaguely remember
the article that I was basing the second method off of (see below comment),
but I don't remember enough of the details to find it again. As such, I will
just recommend subclassing `AdminSite`.

* * *

EDIT 2: After some searching, I found this:

[Does an equivalent of override exist for nested
functions?](http://stackoverflow.com/questions/11911544/does-an-equivalent-of-
override-exist-for-nested-functions)

The 'monkey_patch_fn' function does exactly what you want and demonstrates one
possible approach. It may or may not be complete.

My original plan was to modify the function in place by disassembling it, but
I've been running into issues with the attributes being read-only (which I
_think_ was what my original article dealt with... but I can't find it).

* * *

EDIT 3:

Found another way using a module called `byteplay`. Glad I didn't give up so
soon. I like this way _a lot_ more. It might be just as hacky under the hood,
but I trust a full fledged published module to take more care than a random
answer for a specific question.

Anyway. Because I don't feel like looking at the Django code right now, I will
present an example which should suffice. First, the setup.

    
    
    from byteplay import *
    import dis
    
    def test():
        def printone():
            print 1
        printone()
    
    def printtwo():
            print 2
    
    dis.dis(test)
    

The output here will be

    
    
      2           0 LOAD_CONST               1 (<code object printone at 0x7f72097371b, file "<stdin>", line 1>)
                  3 MAKE_FUNCTION            0
                  6 STORE_FAST               0 (printone)
    
      4           9 LOAD_FAST                0 (printone)
                 12 CALL_FUNCTION            0
                 15 POP_TOP             
                 16 LOAD_CONST               0 (None)
                 19 RETURN_VALUE        
    

So, then we use `byteplay` to convert it to a `Code` object, edit it, then
turn it back into normal bytecode.

    
    
    testcode = Code.from_code(test.__code__)
    print testcode.code
    

The output is:

    
    
    [(SetLineno, 2), (LOAD_CONST, <byteplay.Code object at 0x7f72096e6a50>), (MAKE_FUNCTION, 0), (STORE_FAST, 'printone'), (SetLineno, 4), (LOAD_FAST, 'printone'), (CALL_FUNCTION, 0), (POP_TOP, None), (LOAD_CONST, None), (RETURN_VALUE, None)]
    

which mirrors `dis`. So, we juts need to change the `Code` object in the
second tuple and put the new code into the original object.

    
    
    testcode.code[1] = (LOAD_CONST, Code.from_code(printtwo.__code__))
    test.__code__ = testcode.to_code()
    dis.dis(test)
    test()
    

The output is a little messy, but we see that:

    
    
      5           0 LOAD_CONST               1 (<code object printtwo at 0x7fc668476230, file "byteplaytest.py", line 9>)
                  3 MAKE_FUNCTION            0
                  6 STORE_FAST               0 (printone)
    
      7           9 LOAD_FAST                0 (printone)
                 12 CALL_FUNCTION            0
                 15 POP_TOP             
                 16 LOAD_CONST               0 (None)
                 19 RETURN_VALUE        
    2
    

So, we are loading the `printtwo` function, saving it as a function under the
local variable name `printone` and then the final `2` is just the `printtwo`
function successfully being called.

This should comprehensively illustrate what you need to do. You will need to
use `dis` to determine which line in the bytecode you need to change, but you
should only need to change the `LOAD_CONST`, I think. Granted, I have no tried
this with libraries or anything... but just let me know if there are any
issues.

