Maghoumi
Maghoumi

Reputation: 3380

Replace Python class definition at runtime

This question may sound similar to the following, but I'm not sure how to apply their solutions to my use-case:

How to import a module given the full path?
Can you use a string to instantiate a class?

I have a class Foo defined and imported in my project. However, at runtime I may have a string containing a different definition of Foo (along with lots of other classes and import statements). I'd like to be able to replace the already loaded Foo with the one in my string, in a way that after the operation, anybody who instantiates f = Foo() would instantiate the definition from my string. At the same time, I'd like to ignore any other definitions/imports in my string. How to do this?

Assume the following project structure and use-case:

project/
    __init__.py
    mytypes/
        __init__.py
        foo.py  # contains the definition 'class Foo'
    another_package/
        bar.py
    main.py

Inside main.py and bar.py I have from mytypes.foo import Foo. After the replace operation detailed above I want both to use the new definition of Foo from the replacement string, but no other definition from my string.

Upvotes: 0

Views: 969

Answers (1)

Iain Shelvington
Iain Shelvington

Reputation: 32244

The short answer is: don't do this. You will run into all kinds of strange errors that you will not expect

You can use exec to run some arbitrary code, if you pass a dictionary as the second argument the resulting "globals" from the executed string will be stored in the dictionary

namespace = {}
exec('class Foo:\n    x = 10', namespace)
namespace['Foo']  # This will be a class named Foo

You could then assign this to the module

import your_module
your_module.Foo = namespace['Foo']

Now anywhere that your_module.Foo is accessed you will get the class from your string.

However, your module may have been imported at some time before you have patched it, it's very difficult to be able to say for certain when your module will be imported. Someone may have bound Foo using from your_module import Foo, if this has run before your patch then you will not change this Foo class. Even if you only ever access your_module.Foo, if an instance has been initialised before your patch then any subsequent instances will not even have the same type!

f = your_module.Foo()
# run your patch
isinstance(f, your_module.Foo)  # False

In the case above f is an instance of a completely different type to the current your_module.Foo

Upvotes: 2

Related Questions