Search code examples
pythongdbmonkeypatching

Python: Fix missing import with a monkey patch


I am trying to monkey patch a missing import. The old_invoke() still does not get the import. In case it is relevant, MyClass is a gdb.Command.

(gdb) pi
>>> import mymodule
>>> old_invoke = mymodule.MyClass.invoke
>>> def new_invoke(self, *k, **kw):
...   print("New invoke")
...   import getopt
...   old_invoke(self, *k, **kw)
...
>>> mymodule.MyClass.invoke = new_invoke
>>>
(gdb) my_gdb_command
New invoke
Python Exception <class 'NameError'> name 'getopt' is not defined:
Error occurred in Python: name 'getopt' is not defined

Also, in case it is relevant, the initial files and sourcing looks something like this:

mymodule.py:

import gdb
class MyClass(gdb.Command):
   ...

   def invoke(self, arg, from_tty):
      options, remainder = getopt.getopt(args, 'p:s:t:o:')
      ...

MyClass()

myothermodule.py:

import mymodule
...

Sourcing the above

(gdb) source myothermodule.py

Solution

  • old_invoke is trying to reference mymodule's getopt, which doesn't exist. You need:

    >>> import mymodule
    >>> old_invoke = mymodule.MyClass.invoke
    >>> def new_invoke(self, *k, **kw):
    ...   print("New invoke")
    ...   import getopt
    ...   
    ...   # here
    ...   mymodule.getopt = getopt
    ...   
    ...   old_invoke(self, *k, **kw)
    ...
    >>> mymodule.MyClass.invoke = new_invoke
    

    But, realistically, you should just have an import getopt in mymodule:

    # mymodule.py
    import getopt
    ...
    

    Then your function is simply:

    >>> import mymodule
    >>> old_invoke = mymodule.MyClass.invoke
    >>> def new_invoke(self, *k, **kw):
    ...   print("New invoke")
    ...   old_invoke(self, *k, **kw)
    ...
    >>> mymodule.MyClass.invoke = new_invoke
    

    Addendum

    As another note, using import in a function isn't generally recommended. Unless you are only calling this function once (and even then, why?), every time you call the function you are attempting to load a module, which at best will always do a check against sys.modules when you probably don't have to.

    Have the import getopt at the top of the script:

    import getopt
    import mymodule
    
    mymodule.getopt = getopt
    

    Which is where you'd probably expect this to be anyways