Yes, I have seen:
- monkey-patching python an instance method
 - How to call the original method when it is monkey-patched?
 
... but none of these approaches seem to work in my example:
Basically, this is an example that tries to use QtAwesome to provide a spinning icon to a QMessageBox; there is a class Spin in QtAwesome that does animations, which has a method _update: I would like to monkeypatch this method, so first the original method is called, and then a message is printed - so I have this:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
import qtawesome as qta
class Example(QWidget):
  def __init__(self):
    super().__init__()
    self.initUI()
  def initUI(self):
    self.setGeometry(300, 300, 300, 220)
    self.setWindowTitle('Hello World')
    self.show()
    msgBox = QMessageBox( QMessageBox.Information, "Title", "Content ...", QMessageBox.Cancel )
    orig_ipmsize = msgBox.iconPixmap().size()
    print(orig_ipmsize.width(), orig_ipmsize.height()) # 0 0 for QMessageBox.NoIcon; 32 32 for QMessageBox.Information
    animation = qta.Spin(msgBox, autostart=True)
    DO_MONKEYPATCH = 2 # 1 or 2
    if DO_MONKEYPATCH == 1:
      old_anim_update = animation._update
      def new_anim_update(self):
        old_anim_update(self) # TypeError: Spin._update() takes 1 positional argument but 2 were given
        print("new_anim_update")
      animation._update = new_anim_update.__get__(animation, qta.Spin) # https://stackoverflow.com/a/28127947
    elif DO_MONKEYPATCH == 2:
      def update_decorator(method):
        def decorate_update(self=None):
          method(self) # TypeError: Spin._update() takes 1 positional argument but 2 were given
          print("decorate_update")
        return decorate_update
      animation._update = update_decorator(animation._update) # https://stackoverflow.com/a/8726680
    #print(animation._update)
    spin_icon = qta.icon('fa5s.spinner', color='red', animation=animation)
    msgBox.setIconPixmap(spin_icon.pixmap(orig_ipmsize)) #msgBox.setIcon(spin_icon)
    #animation.start()
    returnValue = msgBox.exec()
if __name__ == '__main__':
  app = QApplication(sys.argv)
  ex = Example()
  sys.exit(app.exec_())
However, no matter which DO_MONKEYPATCH method I choose, I get TypeError: Spin._update() takes 1 positional argument but 2 were given?
OK, just noticed how both errors apply while writing the question, and found if I change the "old method" calls to NOT use a (self) argument -- i.e. I change old_anim_update(self) / method(self), to old_anim_update() / method() -- then both DO_MONKEYPATCH methods allow for running without the positional argument error - however only DO_MONKEYPATCH method 1 seems to preserve self:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
import qtawesome as qta
class Example(QWidget):
  def __init__(self):
    super().__init__()
    self.initUI()
  def initUI(self):
    self.setGeometry(300, 300, 300, 220)
    self.setWindowTitle('Hello World')
    self.show()
    msgBox = QMessageBox( QMessageBox.Information, "Title", "Content ...", QMessageBox.Cancel )
    orig_ipmsize = msgBox.iconPixmap().size()
    print(orig_ipmsize.width(), orig_ipmsize.height()) # 0 0 for QMessageBox.NoIcon; 32 32 for QMessageBox.Information
    animation = qta.Spin(msgBox, autostart=True)
    DO_MONKEYPATCH = 1 # 1 or 2
    if DO_MONKEYPATCH == 1:
      old_anim_update = animation._update
      def new_anim_update(self):
        old_anim_update() # no error
        print("new_anim_update {}".format(self)) # self is <qtawesome.animation.Spin object at 0x00000238f1d45f10>
      animation._update = new_anim_update.__get__(animation, qta.Spin) # https://stackoverflow.com/a/28127947
    elif DO_MONKEYPATCH == 2:
      def update_decorator(method):
        def decorate_update(self=None):
          method() # TypeError: Spin._update() takes 1 positional argument but 2 were given
          print("decorate_update {}".format(self)) # self is None
        return decorate_update
      animation._update = update_decorator(animation._update) # https://stackoverflow.com/a/8726680
    #print(animation._update)
    spin_icon = qta.icon('fa5s.spinner', color='red', animation=animation)
    msgBox.setIconPixmap(spin_icon.pixmap(orig_ipmsize)) #msgBox.setIcon(spin_icon)
    #animation.start()
    returnValue = msgBox.exec()
if __name__ == '__main__':
  app = QApplication(sys.argv)
  ex = Example()
  sys.exit(app.exec_())
So, it seems that DO_MONKEYPATCH == 1 method is the answer to this question as originally posed - but I am still worried, how does my call to old_anim_update(), without any original references to self, call the old method correctly? Or is there a more correct method to do this kind of monkeypatch?
qta.Spin()?