0

My problem is like this: I need to change how a function behave, but I can't access or change the file with which the function is located. I could import it though, and I would like to change the implementation a bit. However, since I can't access the source code itself, I can't change it directly. I want to actually change the function implementation so that all other files that import it will also have the new function. I'm currently trying to manipulate the code bytes of the function's __code__.co_code attribute (I know this is very bad software practice, but I really have no choice.). The __code__.co_code returns a series of bytes characters, which is very incomprehensible and extremely hard to change (I mean, like, how am I going to write a code using bytes??). I would like to inject a new code object into the function. Is there any way to first convert a Python string containing the new implementation to a series of byte characters and then inject it safely into the old function?

For a minimal reproducible example, suppose I have the following function:

def func1():
    return 1

and I want to change it to:

def func1():
    return 0

I've managed to access the byte code sequences of the function's __code__.co_code attribute, like this:

def func1():
    return 1

code_obj = func1.__code__.co_code
print(code_obj) # prints b'd\x01S\x00'
new_code = b'd\x01S\x00' # copied the original code bytes sequence. How am I going to write `return 0` in bytes?
func1.__code__.co_code = new_code
print(func1)
code_obj2 = func1.__code__.co_code

And it gives me this error:

Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm 2024.1\plugins\python\helpers\pydev\pydevconsole.py", line 364, in runcode
    coro = func()
  File "<input>", line 7, in <module>
AttributeError: readonly attribute

It tells me that I cannot change the __code__.co_code attribute of the function. Additionally, I really don't know how to write the new function implementation (return 0) in bytes sequences. I know that I can probably make use of ASTs but I don't know how to use them either. Can anyone help me? I'm really stuck here. (Side note: I can't just shadow the old function with a new implementation because I want other files that import the library to also have the change. Also, I don't want to set the function in the module using import lib; lib.old_function = new_function.)

4
  • You can only replace whole __code__, and that would be marginally easier: just write a new function, take its __code__ and assign. def your_func(...): ... and func1.__code__ = your_func.__code__. Commented Jul 7, 2024 at 1:03
  • 5
    What advantage is there in manipulating the low-level __code__ object instead of just monkeypatching (which you said you do not want to do)? Commented Jul 7, 2024 at 1:24
  • 4
    "I want other files that import the library to also have the change" - This is probably a bad idea. This is bound to have wonky side effects in the future; you're likely better off writing your own functions in a new module and importing those instead. Commented Jul 7, 2024 at 2:16
  • 1
    Going out into the weeds here, but keep in mind that bytecode is an implementation detail of CPython, not something guaranteed by the language. Such code may not work with other implementations, and possibly not even future versions of CPython. Commented Jul 7, 2024 at 13:43

1 Answer 1

0

I tried the following, but I'm not an expert and am not sure if it is bad, or how bad it can be. But anyway, here's my code:

#!/usr/bin/env python3

a=1
print(f"a={a}")                # a=1

def func():
    global a
    a = a+1

print("func()")                # func()
func()
print(f"a={a}")                # a=2

source_code = """
global a
a = a+17
"""
code_obj = compile(source_code, '<string>', 'exec')

print("func.__code__ = code_obj")
func.__code__ = code_obj

print("func()")                # func()
func()
print(f"a={a}")                # a=19

Obviously, The function in this example takes no arguments, and does not explicitly return a value. Please don't ask me how to make it work for a function that takes arguments or returns a value.


P.S., If you use this trick in a program in which the contents of the source_code string could come from anywhere outside of the program, then you are opening a HUGE security hole. If you publish the program, then you are opening that security hole on your customer's computers. If you use the trick in a server or in "the cloud", then you are opening that hole on the hosting company's computers.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.