0

I've been trying to work around a bug in a third party module. The bug was fixed recently, but I've been wondering about my issues nevertheless. Perhaps this question may help me or others in the future.

The module defines a class Runtime with several methods that I use. Upon importing the module it automatically creates an instance of Runtime, loads configuration and gives the instance to the user (me) to work with.

# thirdPartyModule.py

class Runtime:
    def __init__(self, a):
        self.a = a

    def configuration(self, ...):
        ...

    def fun(self):
        print(self.a)

rt = Runtime("twice")
rt.configuration(...)

Unfortunately, one of the runtime methods contained a bug. I used to monkeypatch this bug by just overwriting the method of the instance by a working substitute like so:

# mycode.py

import types
from thirdPartyModule import rt

def newfun(self):
    print(self.a, self.a)

rt.fun = types.MethodType(newfun, rt)
rt.fun()

This worked just fine while I was waiting for the developer to fix the bug. Or at least it did up to the point where the developer added __slots__ to the class:

# thirdPartyModule.py

class Runtime:
    __slots__ = ("a", )

    def __init__(self, a):
        self.a = a

    ...

Since then I've tried several method-overwriting solutions in this forum, but all of them were rejected ("AttributeError: 'Runtime' object attribute 'fun' is read-only").

It might be that unittest.mock can help me out, but I'm not too familiar with mocking. I've tried the following without success:

import unittest.mock

rt.fun = mock.Mock(rt.fun, side_effect=newfun)

This results in the same AttributeError. Perhaps I could mock the entire instance (rt = mock.Mock(rt)) or something similar, but I'm neither familiar nor completely comfortable with this approach.

As I said the bug has been fixed in the meantime, but I cannot help but wonder how you would proceed in this situation?

Bart
  • 3
  • 1

1 Answers1

4
>>> class X():
...     __slots__ = ("a",)
...     def func():
...         print("1")
... 
>>> x = X()
>>> type(x)
<class '__main__.X'>
>>> type(x).func = lambda self: print("2")
>>> x.func
<bound method <lambda> of <__main__.X object at 0x7f27fb88b050>>
>>> x.func()
2
>>> 

Explanation: you have to monkeypatch the Runtime class instead of the instance.

Hack5
  • 3,244
  • 17
  • 37