[Python] python -m 與 import 誤用當前目錄下套件的問題

[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"

 

參考資料:

Python: Modules

PEP 0328 — Imports: Multi-Line and Absolute/Relative

stackoverflow: Import python package from local directory into interpreter

(本頁面已被瀏覽過 240 次)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料