Created
August 22, 2021 09:39
-
-
Save Aragroth/0b4d1a6b97a633643b7b83e89a5da64f to your computer and use it in GitHub Desktop.
Change local variables of function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import gc | |
| import ctypes | |
| import sys | |
| import dis | |
| def change_locals(**new_locals): | |
| def modify_function(func): | |
| consts_arr = func.__code__.co_consts # all local-const values in funciton body | |
| # sample idea of getting names, that correspond to local-const values | |
| bytecode = dis.Bytecode(func) | |
| position, params_order = 1, {} | |
| last_operation_const_loading = False | |
| for instr in bytecode: | |
| if instr.opname == 'LOAD_CONST': | |
| last_operation_const_loading = True | |
| elif instr.opname == 'STORE_FAST': | |
| if last_operation_const_loading: | |
| params_order.update({instr.argval: position}) | |
| position += 1 | |
| else: | |
| last_operation_const_loading = False | |
| # maybe it is not needed? | |
| gc.disable() | |
| # PyTuple_SetItem works only on tuples with RefCount = 1 (newly created tuples), but | |
| # only works when decreasing to 2 links. (May be sys.getrefcount creates additional link?) | |
| modifying = ctypes.py_object(consts_arr) | |
| decreased_count = 0 | |
| while sys.getrefcount(consts_arr) > 2: | |
| ctypes.pythonapi.Py_DecRef(modifying) | |
| decreased_count += 1 | |
| for var_name in new_locals: | |
| if var_name not in func.__code__.co_varnames: | |
| continue | |
| replacing = new_locals[var_name] | |
| repl = ctypes.py_object(replacing) | |
| ctypes.pythonapi.Py_IncRef(repl) # not increasing link will cause to future Segfault, when program finished | |
| ctypes.pythonapi.PyTuple_SetItem(modifying, ctypes.c_ssize_t(params_order[var_name]), repl) | |
| # reincreasing links count to object | |
| for _ in range(decreased_count): | |
| ctypes.pythonapi.Py_IncRef(modifying) | |
| gc.enable() | |
| def execute_func(*args, **kwargs): | |
| return func(*args, **kwargs) | |
| return execute_func | |
| return modify_function | |
| @change_locals(x=150, c='Has changed') | |
| def function(): | |
| c = 'Value' | |
| x = 2 | |
| f = x | |
| print(x + f, c) | |
| function() # Output: 300 Has changed |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment