How can I transparently redirect a Python import?
I'm looking for a way to emulate symlinks for Python imports. I'd like to be able to unzip the following folder structure in-place without duplicating files:
root
├─ python_lib
│ └─ my_utils
│ ├─ __init__.py
│ └─ etc.py
├─ app1
├─ app2
└─ app3
├─ lib
│ ├─ __init__.py
│ └─ my_utils.py
└─ run.py
app3/ru开发者_如何学Cn.py
contains this:
from lib.my_utils import etc
etc.pancakes()
I'd like the code to use the etc
located in python_lib/my_utils/
. Is there anything I can put in app3/lib/my_utils.py
so that Python >= 3.1 will transparently import the python_lib/my_utils/
folder (using relative paths and ..
), and subpackages will also work?
You will have to execute something before app3/run.py
reaches the import statement.
import python_lib
import sys
sys.modules['lib'] = python_lib
# ...
from lib import etc
print etc.__file__
print dir(etc)
You should add this path into sys.path
. For example:
lib_path = os.path.abspath( os.path.split( os.getcwd()+"/"+sys.argv[0] )[0]+"/../_lib/my_utils/" )
sys.path.append(lib_path)
Add __init__.py
to the python_lib
folder
app3/run.py
contains
import python_lib.my_utils
import os
sys.modules['lib.my_utils'] = python_lib.my_utils
I made the following changes to your directory structure to simplify the example:
- create root/python_lib/__init__.py
- rename root/python_lib to root/lib
- replace root/app3/lib/ with lib.py
Place the following code in root/app3/lib.py
import os
import sys
pth = os.path.sep.join(sys.argv[0].split(os.path.sep)[0:-2])
sys.path.insert(0, pth)
del sys.modules[__name__]
import lib
Essentially, we have a dummy module that replaces its own reference in sys.modules with a reference to a module or package in another location.
How about this? (Yes, put it at app3/lib/my_utils.py
.)
import os
_f = os.path.realpath(__file__)
_f = os.path.dirname(_f)
_f = os.path.dirname(_f)
_f = os.path.dirname(_f)
_f = os.path.join(_f, 'python_lib')
def f():
path = sys.path
path.insert(0, _f)
sys.modules['lib.my_utils'] = __import__('my_utils')
path.pop(0)
f()
Very late to this, but this is incredibly easy. All you need to do is modify the __path__
attribute of your package so it points to the folder where your modules are. This will require lib
to be a package, not just a module (ie. a folder with an __init__.py
). By default __path__
is a one element list that contains the path of folder that is the package -- ['/path/to/lib']
. Meaning, that by default, python searches for sub modules in the module folder (seems like a very logical thing to do).
You can alter the contents of path to point to where ever you want, but it must contain absolute paths. If you only append path/to/lib_python
then python will search lib first, find my_utils.py and then stop. So in that case you will need to delete my_utils.py
or put the new path at the front of __path__
so that location is searched first. This also means that the lib
folder can contain its own supplementary modules to the shared libraries and still work.
In practice:
lib/__init__.py
from os.path import join
__path__.insert(0, join(__path__[0], "..", "..", "python_lib"))
精彩评论