[Python] python -m 與 import 誤用當前目錄下套件的問題
最近在查一個專案程式很怪的問題…
我們的程式會去 import 自己的一個 module (假設叫 tool/mymodule),
平常執行沒有問題,但是如果到一個特殊目錄裡,
裡面也有一個 tool 目錄,而 tool 目錄下也有一個 __init__.py 的話,
用 python -m 執行這個程式就會出錯,但用 python xxx.py 的方式就不會…
下面用個例子來看看吧~
我在 /tmp 目錄下面建立了下面幾個檔案:
– pkg/__init__.py
– pkg/main.py
– pkg/tool/__init__.py
– pkg/tool/pycurl.py
– tool/__init__.py
pkg/main.py 的內容如下:
import os import sys sys.path.append(os.path.dirname(__file__)) print sys.path from tool import pycurl print "main"
pkg/tool/pycurl.py 的內容如下:
print "pycurl"
而每一個 __init__.py 的內容都是一樣的,
目的只是讓 /tmp/pkg 和 /tmp/tool 目錄都變成是 python 的 package~
print "__init__ of %s" % __file__
開始實驗吧~
1. 將工作目錄切換到 /tmp/pkg,執行 python main.py
注意 /tmp/pkg (也就是 script 所在的目錄) 被加到 sys.path 的最前面,執行沒有問題:
['/tmp/pkg', '/usr/lib/python2.7/site-packages/polib-1.0.6-py2.7.egg', '/usr/lib/python2.7/site-packages/pip-8.0.2-py2.7.egg', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/python2.7/site-packages', '/usr/lib64/python2.7/site-packages/gtk-2.0', '/usr/lib/python2.7/site-packages', ''] __init__ of /tmp/pkg/tool/__init__.pyc pycurl main
2. 將工作目錄切換到 /tmp/pkg,執行 python -m main
會有個空字串 “” (代表的是目前的工作目錄) 被加到 sys.path 的最前面,
依照網路上文章的理解,如果 script 所在的目錄無法取得的話,
那麼 script 所在的目錄不會被加到 sys.path 裡面,而是會將當前的目錄 (“”) 加進去~
目前看起來 -m 就會有 script 所在目錄無法取得的效果…
而因為當前目錄 (/tmp/pkg) 下有個 tool 的 package,
裡面也的確有個 pycurl.py,因此 from tool import pycurl 並沒有問題:
['', '/usr/lib/python2.7/site-packages/polib-1.0.6-py2.7.egg', '/usr/lib/python2.7/site-packages/pip-8.0.2-py2.7.egg', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/python2.7/site-packages', '/usr/lib64/python2.7/site-packages/gtk-2.0', '/usr/lib/python2.7/site-packages', '/tmp/pkg'] __init__ of tool/__init__.pyc pycurl main
3. 將工作目錄切換到 /tmp,執行 python pkg/main.py
script 所在的目錄 /tmp/pkg 依然被加到 sys.path 的最前面,
from tool import pycurl 也是從 /tmp/pkg 這邊發現有 tool/pycurl.py,因此 import 沒有問題:
['/tmp/pkg', '/usr/lib/python2.7/site-packages/polib-1.0.6-py2.7.egg', '/usr/lib/python2.7/site-packages/pip-8.0.2-py2.7.egg', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/python2.7/site-packages', '/usr/lib64/python2.7/site-packages/gtk-2.0', '/usr/lib/python2.7/site-packages', 'pkg'] __init__ of /tmp/pkg/tool/__init__.pyc pycurl main
4. 將工作目錄切換到 /tmp,執行 python -m pkg/main
當前目錄 (“”) 被加到 sys.path 的最前面,因此在執行 from tool import pycurl 時,
會先搜尋目前目錄 (/tmp) 下是否有 tool 這個 package,
而因為的確有 /tmp/tool 目錄 (且裡面有 __init__.py),因此會認為 tool package 指的就是 /tmp/tool,
但接下來要從 tool 裡面再 import pycurl 時,就找不到了:
['', '/usr/lib/python2.7/site-packages/polib-1.0.6-py2.7.egg', '/usr/lib/python2.7/site-packages/pip-8.0.2-py2.7.egg', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/python2.7/site-packages', '/usr/lib64/python2.7/site-packages/gtk-2.0', '/usr/lib/python2.7/site-packages', '/tmp/pkg'] __init__ of tool/__init__.pyc Traceback (most recent call last): File "/usr/lib64/python2.7/runpy.py", line 162, in _run_module_as_main "__main__", fname, loader, pkg_name) File "/usr/lib64/python2.7/runpy.py", line 72, in _run_code exec code in run_globals File "/tmp/pkg/main.py", line 8, in <module> from tool import pycurl ImportError: cannot import name pycurl
目前的暫時解法是將 “” 從 sys.path 移掉,
這樣就能避免從當前目錄下,去尋找 tool package 了:
import os import sys sys.path.append(os.path.dirname(__file__)) # Remove current directory from sys.path if "" in sys.path: sys.path.remove("") print sys.path from tool import pycurl print "main"
參考資料:
PEP 0328 — Imports: Multi-Line and Absolute/Relative
stackoverflow: Import python package from local directory into interpreter