Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 46 lines (35 sloc) 1.621 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
# test this module by:
# python -c "import test_importearlyexit"
# the wanted output is just 'mod: start'

print "mod: start"

def breakModule():
"""
Be careful using this! It manipulates the code string of the code object
of the caller frame! It will overwrite the next 3 bytecodes.
There are a few expectations that this work:
- inspect.currentframe().f_back must return the caller frame
- id(code.co_code) must return the native address of the code string
- the C-API PyString_AsString must be available and callable via ctypes
Discussion: The bytecode for Python modules allows the op RETURN_VALUE.
This is what you see at the compiled bytecode at the end of a module.
I wonder why `return` is not allowed at the module source level.
A normal `return` in a module should just work.
"""
import inspect
frame = inspect.currentframe()
frame = frame.f_back # manipulate the caller frame
code = frame.f_code

import ctypes
ctypes.pythonapi.PyString_AsString.argtypes = (ctypes.c_void_p,)
ctypes.pythonapi.PyString_AsString.restype = ctypes.POINTER(ctypes.c_char)

targetjumpptr = len(code.co_code) - 4 # LOAD_CONST+RETURN_VALUE at end
iptr = frame.f_lasti + 3 # this is a funccall, so advance by 3
assert iptr + 3 <= targetjumpptr # otherwise we don't have enough room

import dis
codestr = ctypes.pythonapi.PyString_AsString(id(code.co_code))
codestr[iptr] = chr(dis.opmap["JUMP_ABSOLUTE"])
codestr[iptr+1] = chr(targetjumpptr & 255)
codestr[iptr+2] = chr(targetjumpptr >> 8)


if True: # check if we should skip module loading here
breakModule()

print "mod: after early exit"
Something went wrong with that request. Please try again.