Suppose mine.py wants to import moduleA and moduleB but moduleA and moduleB each try to import a module called "moduleC". These are two distinct modules that both happen to be named "moduleC". When mine.py is run, depending on sys.path either moduleA or moduleB gets the correct "moduleC", the other gets a surprise, and chaos ensues.
If moduleA and moduleB were written by different authors, neither of which is us, it is preferable to not modify those modules. Is there any solution available to the author of mine.py that does not modify moduleA or moduleB?
The following questions ask how to solve this issue when you are the author of moduleA or moduleB.
Importing from builtin library when module with same name exists
How to access a standard-library module in Python when there is a local module with the same name?
My specific case
I want to run a program called PyMOL under the Python debugger, pdb. PyMOL unfortunately has a "cmd.py" that it imports and that conflicts with the usual cmd which pdb imports.
The relevant parts of the PyMOL install look like this:
pymol/
__init__.py
cmd.py
PyMOL is run by executing __init__.py
. This file then imports cmd
as from pymol import cmd
.
Working from what BrenBarn pointed out, so far I can get pdb
to successfully import the correct cmd
by temporarily removing the pymol
directory from the front of sys.path
. After that when PyMOL tries to import its cmd
it crashes. Somehow I need to remove Python's cmd
from the import module search before PyMOL
imports but after pdb
imports.
Minimal example
$ ls
pymol/
$ ls pymol/
__init__.py cmd.py
init.py
# insert some code into __init__.py directly
import sys
pymol_path = sys.path[0]
sys.path[0] = ""
import pdb
sys.path[0] = pymol_path
from pymol import cmd
# test a sandwich of calls that require each "cmd" modules
pdb.set_trace()
cmd.foo()
pdb.set_trace()
cmd.foo()
print "done!"
# original PyMOL __init__.py code would follow
cmd.py
def foo():
print("cmd.foo()")
Try it
$ PYTHONPATH= python ./pymol/__init__.py
> /Users/khouli/scr/pymol_scr/pymol/__init__.py(11)<module>()
-> cmd.foo()
(Pdb) continue
cmd.foo()
> /Users/khouli/scr/pymol_scr/pymol/__init__.py(13)<module>()
-> cmd.foo()
(Pdb) continue
cmd.foo()
done!
Edit: The method given above now seems to work but as BrenBarn's answer states, there likely is no solution that leaves all third party code unmodified as the question originally asked for. This is due to quirks in PyMOL.
Your problem is not just with the imports, but with the fact that you are running
__init__.py
as a script. When you run a script, Python adds the directory containing the script to the front ofsys.path
, and this globally affects all subsequent imports.There is no way to customize anything if you are directly running a file that you don't want to modify. You can't do any sneaky
sys.path
manipulations unless you get to run your own code first, to set up the path the way you want it. If you import the file instead of running it, you have the possibility to use your own code to tweak the paths.I suspect that this problem is to some degree specific to PyMOL, which unfortunately does not seem to be well-designed in this regard. Looking at the source code here, I see that PyMOL's
__init__.py
includes a lot of custom code that does weird things likeimport __main__
and checking whether the version of itself that is running has various attributes. You could try using the "unsupported/experimental" method described in the comments in that file, which involves importing PyMOL instead of running it. I don't know anything about PyMOL so I don't know how that would work.It might be worth contacting the authors of PyMOL to suggest that they fix this.