开发者

How can I transparently redirect a Python import?

开发者 https://www.devze.com 2023-02-08 06:15 出处:网络
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:

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"))
0

精彩评论

暂无评论...
验证码 换一张
取 消