Reputation: 543
I'm importing a library someone else made and I'm wanting to change the way a particular class method works from that library so I've copied the class method to my own file and wanting to replace it at runtime. This seems to work just fine for functions but seems to fall apart for class methods.
a.library.package.library_file.py
class LibraryClass(ParentClass):
@classmethod
def get_cost(cls, time):
return int(time * cls.hourly)
I'm wanting to replace it with this
class LibraryClass(ParentClass):
@classmethod
def get_cost(cls, time):
return 1234
I've tried to just do a normal replace which works just fine for regular functions
import a.library.package.library_file
...
a.library.package.library_file.LibraryClass.get_cost = get_cost
But it doesn't seem to work right at all, the method is called with the wrong arguments at the wrong time and results in a crash. After some research on Google, StackOverflow, and Python I began trying to use the mock classes.
from unittest.mock import patch
@patch.object('a.library.package.library_file.LibraryClass', 'get_cost')
def get_cost(cls, time):
return 1234
The good news is it doesn't crash, bad news is it doesn't do anything, the old code is still there and it's like my code doesn't exist.
I've tried all kinds of other ways to do this such as
import a.library.package.library_file
@patch.object(a.library.package.library_file.LibraryClass, 'get_cost')
...
or
from a.library.package.library_file import LibraryClass
@patch.object(LibraryClass, 'get_cost')
...
but every time the method is never touched. It's like my code doesn't exist and the old code is used.
Upvotes: 11
Views: 6994
Reputation: 543
It turns out the solution was simple as I thought it might be. It took more than a day of digging and there's just no help anywhere online but I found an obscure random post from china on page 2 of Google which finally answered my question. Link Here
This is the resulting code that works
from types import MethodType
from a.library.package.library_file.py import LibraryClass
def get_cost(cls, time):
return 1234
LibraryClass.get_cost = MethodType(get_cost, LibraryClass)
It's the same as replacing a function only you need to wrap it in "MethodType" because the code your swapping out has "classmethod" ties to the class and so the new code needs to be tied the same in order to work.
Upvotes: 20
Reputation: 2364
Can you just import the original class and create your own class that inherits from the original class ? Then you redefine the method that you want to override and just use your custom class instead of the original class:
from a_library import LibraryClass
class YourClass(LibraryClass):
@classmethod
def get_cost(cls, time):
# redefine the method here
return 1234
cost = YourClass.get_cost(time)
Then just use YourClass
instead of the library class. It will behave exactly like the library class, but with your method instead of the original method.
Upvotes: 1