Search code examples
pythonpython-module

Import shadowed module into shadowing module which is in sys.path


There are similar questions about importing an intentionally shadowed module into a shadowing module which is not in sys.path. That's pretty easy; just use:

from __future__ import absolute_import

Here's my problem: my module is in sys.path, and it's higher up in sys.path, and that's intentional!

To be more specific, I'm writing a python interpreter which lacks access to stdin, but it can use Tkinter. Not having access to stdin means I can't just use normal input, raw_input, and getpass. I need to replace those with my own methods. The first two are rather easy to handle - I just replace them in __builtin__. getpass isn't as easy though - the user may or may not import it, but when they do, I need to make sure that what they actually import is my module, not the standard one, so that I can get their input through a Tkinter Entry instead of stdin. That part I have handled. The part that I can't figure out is how to pass everything else through to the standard getpass, IE, getuser() should be handled by the real getpass.

Here's what I tried:

from __future__ import absolute_import
import sys
pathToThisGetpass = sys.path.pop(1)    # I know that the shadowing getpass is at index 1.
import getpass as realGetpass          # Since the path doesn't contain this getpass, this should be the real one, right?
sys.path.insert(1, pathToThisGetpass)  # Put this getpass module back into the path again

def getuser():
    print("I am " + str(getuser))
    print("I should call " + str(realGetpass.getuser))
    return realGetpass.getuser()

I added in those two print statements just so I could see why getuser() was failing, and I found that it would print this:

I am <function getuser at 0x026D8A30>
I should call <function getuser at 0x026D8A30>

Plus I end up with infinite recursion.

So... any ideas how I can resolve this?

To try reproducing this, write a file like the one I have above, name it getpass.py, then add the folder that contains it to your sys.path, then run:

import getpass
getpass.getuser()

Solution

  • (Comments posted as an answer:)

    Use imp.load_module and load the module you want, ignoring the standard search.

    If you somehow learn the path of the file to import (e.g. by searching at sys.path on your own), you can just open that file, say something like

    with open(off_path_name) as shadowed_module_file:
      shadow_getpass = imp.load_module(
          'shadowed.getpass',    # the name reported by the module, rather arbitrary
          shadowed_module_file,  # where we are reading it from
          '', # we don't plan to reload this module normally, no suffix
          ('', 'r', imp.PY_COMPILED) # Assuming it's getpass.pyc, use PY_COMPILED
      )
    

    Then use shadow_getpass as you would a normal module.

    Error-handling is omitted, but it's not hard to catch appropriate exceptions.