fork(1) download
  1. import inspect
  2. import types
  3. import ctypes
  4. import sys
  5. import traceback
  6.  
  7.  
  8. frameobject_fields = [
  9. # PyObject_VAR_HEAD
  10. ("ob_refcnt", ctypes.c_int64),
  11. ("ob_type", ctypes.py_object),
  12. ("ob_size", ctypes.c_ssize_t),
  13. # struct _frame *f_back; /* previous frame, or NULL */
  14. ("f_back", ctypes.c_void_p),
  15. # PyCodeObject *f_code; /* code segment */
  16. ("f_code", ctypes.c_void_p),
  17. # PyObject *f_builtins; /* builtin symbol table (PyDictObject) */
  18. ("f_builtins", ctypes.py_object),
  19. # PyObject *f_globals; /* global symbol table (PyDictObject) */
  20. ("f_globals", ctypes.py_object),
  21. ####
  22. ("f_locals", ctypes.py_object),
  23. ("f_valuestack", ctypes.POINTER(ctypes.py_object)),
  24. ("f_stacktop", ctypes.POINTER(ctypes.py_object)),
  25. ("f_trace", ctypes.py_object),
  26. ("f_exc_type", ctypes.py_object),
  27. ("f_exc_value", ctypes.py_object),
  28. ("f_exc_traceback", ctypes.py_object),
  29. ("f_tstate", ctypes.c_void_p),
  30. ("f_lasti", ctypes.c_int),
  31. ]
  32. if hasattr(sys, "getobjects"):
  33. # This python was compiled with debugging enabled.
  34. frameobject_fields = [
  35. ("_ob_next", ctypes.c_void_p),
  36. ("_ob_prev", ctypes.c_void_p),
  37. ] + frameobject_fields
  38. class PyFrameObject(ctypes.Structure):
  39. _fields_ = frameobject_fields
  40.  
  41.  
  42. class Continuation:
  43. def __init__(self, frame):
  44. self.frame = frame
  45. self.lasti = frame.f_lasti
  46. self.lastis = []
  47.  
  48. frame = frame.f_back
  49. while frame is not None:
  50. self.lastis.append(frame.f_lasti)
  51. frame = frame.f_back
  52.  
  53. def __call__(self):
  54. print('\nbefore')
  55. traceback.print_stack()
  56.  
  57. cur_frame = PyFrameObject.from_address(id(inspect.currentframe()))
  58. PyFrameObject.from_address(cur_frame.f_back).ob_refcnt -= 1
  59. cur_frame.f_back = id(self.frame)
  60. PyFrameObject.from_address(id(self.frame)).ob_refcnt += 1
  61.  
  62. frame = self.frame
  63. _frame = PyFrameObject.from_address(id(frame))
  64. _frame.f_lasti = self.lasti + 4
  65.  
  66. frame = frame.f_back
  67. for lasti in self.lastis:
  68. if len(frame.f_code.co_code) != frame.f_lasti + 2:
  69. break
  70. _frame = PyFrameObject.from_address(id(frame))
  71. _frame.f_lasti = lasti + 4
  72. frame = frame.f_back
  73.  
  74. print('\nafter')
  75. traceback.print_stack()
  76.  
  77.  
  78. def callcc(f):
  79. f(Continuation(inspect.currentframe().f_back))
  80.  
  81.  
  82. cc = None
  83.  
  84.  
  85. def func():
  86. bar = 0
  87. print("This should show only once")
  88. def save_cont(k):
  89. global cc
  90. cc = k
  91. callcc(save_cont)
  92. print(bar)
  93. bar += 1
  94.  
  95.  
  96. def g():
  97. func()
  98. print("This should show multiple times")
  99.  
  100. sys.stderr = sys.stdout
  101. g()
  102. cc()
Success #stdin #stdout 0.02s 33448KB
stdin
Standard input is empty
stdout
This should show only once
0
This should show multiple times

before
  File "./prog.py", line 102, in <module>
  File "./prog.py", line 55, in __call__

after
  File "./prog.py", line 102, in <module>
  File "./prog.py", line 98, in g
  File "./prog.py", line 92, in func
  File "./prog.py", line 75, in __call__