Pythonで現在のスクリプトディレクトリを決定する最良の方法は何ですか?
pythonコードを呼び出す多くの方法が原因で、良い解決策を見つけるのが難しいことがわかりました。
以下にいくつかの問題を示します。
exec
、execfile
でスクリプトが実行された場合、__file__
は定義されません。__module__
はモジュールでのみ定義されますユースケース:
./myfile.py
python myfile.py
./somedir/myfile.py
python somedir/myfile.py
execfile('myfile.py')
(別のスクリプトから。別のディレクトリに配置でき、別の現在のディレクトリを持つことができます。完璧な解決策はないことは知っていますが、ほとんどの場合を解決する最適なアプローチを探しています。
最も使用されるアプローチはos.path.dirname(os.path.abspath(__file__))
ですが、exec()
を使用して別のスクリプトからスクリプトを実行する場合、これは実際には機能しません。
現在のディレクトリを使用するソリューションはすべて失敗します。これは、スクリプトの呼び出し方法によって異なる場合があります。または、実行中のスクリプト内で変更することもできます。
os.path.dirname(os.path.abspath(__file__))
確かにあなたが得ようとしている最高です。
exec
/execfile
;を使用してスクリプトを実行することはまれです。通常、モジュールインフラストラクチャを使用してスクリプトをロードする必要があります。これらのメソッドを使用する必要がある場合は、スクリプトに渡すglobals
に__file__
を設定して、そのファイル名を読み取れるようにすることをお勧めします。
実行されたコードでファイル名を取得する他の方法はありません。ご指摘のとおり、CWDは完全に異なる場所にある可能性があります。
execfile(...)
を介してスクリプトが呼び出される場合を本当にカバーしたい場合は、inspect
モジュールを使用してファイル名(パスを含む)を推測できます。私の知る限り、これはあなたがリストしたすべてのケースで機能します:
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
#!/usr/bin/env python
import inspect
import os
import sys
def get_script_dir(follow_symlinks=True):
if getattr(sys, 'frozen', False): # py2exe, PyInstaller, cx_Freeze
path = os.path.abspath(sys.executable)
else:
path = inspect.getabsfile(get_script_dir)
if follow_symlinks:
path = os.path.realpath(path)
return os.path.dirname(path)
print(get_script_dir())
CPython、Jython、Pypyで動作します。 execfile()
を使用してスクリプトを実行すると機能します(sys.argv[0]
および__file__
ベースのソリューションはここで失敗します)。スクリプトが 実行可能なZipファイル(/卵) 内にある場合に機能します。スクリプトがZipファイルから「インポート」(PYTHONPATH=/path/to/library.Zip python -mscript_to_run
)されている場合に機能します。この場合、アーカイブパスを返します。スクリプトがスタンドアロンの実行可能ファイル(sys.frozen
)にコンパイルされている場合に機能します。シンボリックリンクに対して機能します(realpath
はシンボリックリンクを削除します)。インタラクティブなインタープリターで動作します。この場合、現在の作業ディレクトリを返します。
Python 3.4+では、より単純な pathlib
モジュールを使用できます。
from inspect import currentframe, getframeinfo
from pathlib import Path
filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent
os.path...
アプローチは、Python 2の「完了したもの」でした。
Python 3では、次のようにスクリプトのディレクトリを見つけることができます。
from pathlib import Path
cwd = Path(__file__).parents[0]
os.path.dirname(os.path.abspath(__file__))
を使用し、exec
を使用する場合に本当に必要があるかどうかを慎重に調べてください。スクリプトをモジュールとして使用できない場合、問題のあるデザインの兆候である可能性があります。
Zen of Python#8を念頭に置いてください。exec
で機能する必要があるユースケースに適切な引数があると思われる場合は、問題の背景についての詳細を知っています。
します
import os
cwd = os.getcwd()
あなたがしたいことをしますか? 「現在のスクリプトディレクトリ」とはどういう意味かわかりません。あなたが与えたユースケースに対して期待される出力は何でしょうか?
import os
import sys
def get_script_path():
return os.path.dirname(os.path.realpath(sys.argv[0]))
my_script_dir = get_script_path()
print my_script_dir
これにより、スクリプトのディレクトリがスタックの最上部に表示されます(つまり、実行されているもの-Pythonではなく、通常は最初に実行され、C:/
を返します)
まず、匿名コードを挿入する方法について話している場合、ここでいくつかのユースケースがありません。
code.compile_command()
code.interact()
imp.load_compiled()
imp.load_dynamic()
imp.load_module()
__builtin__.compile()
loading C compiled shared objects? example: _socket?)
しかし、本当の質問は、あなたの目標は何ですか-何らかのセキュリティを強化しようとしていますか?または、ロードされるものに興味がありますか?.
security に興味がある場合、exec/execfileを介してインポートされるファイル名は重要ではありません-次を提供する rexec を使用する必要があります。
このモジュールには、標準のPython関数eval()、execfile()の制限付きバージョンであるr_eval()、r_execfile()、r_exec()、r_import()メソッドをサポートするRExecクラスが含まれています。 execおよびimportステートメント。この制限された環境で実行されるコードは、安全と見なされるモジュールと機能にのみアクセスできます。必要に応じて、RExecのサブクラスを追加または削除できます。
しかし、もしこれが学問的追求の場合は、ここで少し掘り下げることができるかもしれないいくつかの間抜けなアプローチがあります。
サンプルスクリプト:
./ deep.py
print ' >> level 1'
execfile('deeper.py')
print ' << level 1'
./ deeper.py
print '\t >> level 2'
exec("import sys; sys.path.append('/tmp'); import deepest")
print '\t << level 2'
/ tmp/deepest.py
print '\t\t >> level 3'
print '\t\t\t I can see the earths core.'
print '\t\t << level 3'
./ codespy.py
import sys, os
def overseer(frame, event, arg):
print "loaded(%s)" % os.path.abspath(frame.f_code.co_filename)
sys.settrace(overseer)
execfile("deep.py")
sys.exit(0)
出力
loaded(/Users/synthesizerpatel/deep.py)
>> level 1
loaded(/Users/synthesizerpatel/deeper.py)
>> level 2
loaded(/Users/synthesizerpatel/<string>)
loaded(/tmp/deepest.py)
>> level 3
I can see the earths core.
<< level 3
<< level 2
<< level 1
もちろん、これはリソース集約的な方法であり、すべてのコードをトレースすることになります。あまり効率的ではありません。しかし、私はそれが斬新なアプローチだと思います。なぜなら、あなたが巣の中に深く入っても機能し続けるからです。 「eval」をオーバーライドすることはできません。ただし、canexecfile()をオーバーライドできます。
このアプローチはexec/execfileのみを対象とし、「インポート」は対象としていないことに注意してください。より高いレベルの「モジュール」ロードフックでは、use sys.path_hooks (PyMOTWの書き込み礼儀)を使用できる場合があります。
それが私の頭上にあるすべてです。
これはほとんどの場合に機能するはずです:
import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
ここに部分的な解決策がありますが、これまでに公開されたすべての解決策よりも優れています。
import sys, os, os.path, inspect
#os.chdir("..")
if '__file__' not in locals():
__file__ = inspect.getframeinfo(inspect.currentframe())[0]
print os.path.dirname(os.path.abspath(__file__))
これですべての呼び出しが機能するようになりましたが、誰かがchdir()
を使用して現在のディレクトリを変更すると、これも失敗します。
ノート:
sys.argv[0]
は機能しません。python -c "execfile('path-tester.py')"
でスクリプトを実行すると-c
が返されますうまくいけば、これが役立つ:-どこからでもスクリプト/モジュールを実行すると、スクリプトの場所を表すモジュール変数である__file__
変数にアクセスできるようになります。
一方、インタープリターを使用している場合は、その変数にアクセスできません。名前[NameError
およびos.getcwd()
を取得すると、間違ったディレクトリが表示されます別の場所からファイルを実行する。
これ ソリューションは、すべての場合にあなたが探しているものを提供するはずです:
from inspect import getsourcefile
from os.path import abspath
abspath(getsourcefile(lambda:0))
完全にテストしたわけではありませんが、問題は解決しました。