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?
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.