Как правильно определить текущий каталог script?
Я хотел бы посмотреть, как лучше определить текущий каталог скриптов в Python?
Я обнаружил, что из-за множества способов вызова кода на Python трудно найти хорошее решение.
Вот некоторые проблемы:
-
__file__
не определен, если скрипт выполняется с exec
, execfile
-
__module__
определяется только в модулях
Случаи применения:
-
./myfile.py
-
python myfile.py
-
./somedir/myfile.py
-
python somedir/myfile.py
-
execfile('myfile.py')
(из другого скрипта, который может находиться в другом каталоге и может иметь другой текущий каталог.
Я знаю, что не существует идеального решения, но я ищу лучший подход, который решает большинство случаев.
Наиболее используемый подход - os.path.dirname(os.path.abspath(__file__))
но он действительно не работает, если вы выполняете скрипт из другого с помощью exec()
.
Предупреждение
Любое решение, использующее текущую директорию, потерпит неудачу, это может отличаться в зависимости от того, как вызывается скрипт, или его можно изменить внутри запущенного скрипта.
Ответы
Ответ 1
os.path.dirname(os.path.abspath(__file__))
действительно лучшее, что вы собираетесь получить.
Необычно выполнять script с exec
/execfile
; обычно вы должны использовать инфраструктуру модуля для загрузки скриптов. Если вы должны использовать эти методы, я предлагаю установить __file__
в globals
, который вы передаете в script, чтобы он мог прочитать это имя файла.
Нет другого способа получить имя файла в исполняемом коде: как вы заметили, CWD может находиться в совершенно другом месте.
Ответ 2
Если вы действительно хотите рассмотреть случай, когда < script вызывается через execfile(...)
, вы можете использовать модуль inspect
для вывода имени файла (включая путь). Насколько мне известно, это будет работать для всех перечисленных вами случаев:
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
Ответ 3
#!/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. Он работает, если script выполняется с использованием execfile()
(sys.argv[0]
и __file__
-установленных решений). Он работает, если script находится внутри исполняемого zip файла (/яйца). Он работает, если script "импортирован" (PYTHONPATH=/path/to/library.zip python -mscript_to_run
) из zip файла; он возвращает путь к архиву в этом случае. Он работает, если script скомпилирован в автономный исполняемый файл (sys.frozen
). Он работает для символических ссылок (realpath
устраняет символические ссылки). Он работает в интерактивном интерпретаторе; он возвращает текущий рабочий каталог в этом случае.
Ответ 4
В Python 3. 4+ вы можете использовать более pathlib
модуль pathlib
:
from inspect import currentframe, getframeinfo
from pathlib import Path
filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent
Ответ 5
Просто используйте os.path.dirname(os.path.abspath(__file__))
и очень внимательно изучите, есть ли реальная потребность в случае, когда используется exec
. Это может быть признаком проблемного дизайна, если вы не можете использовать ваш script в качестве модуля.
Имейте в виду Zen of Python # 8, и если вы считаете, что есть хороший аргумент для прецедента, где он должен работать для exec
, то, пожалуйста, дайте нам знать более подробную информацию о предыстории проблемы.
Ответ 6
Вначале.. пара пропустит прецеденты здесь, если мы говорим о способах ввода анонимного кода.
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, который предлагает следующее:
Этот модуль содержит класс RExec, который поддерживает r_eval(), r_execfile(), r_exec() и r_import(), которые являются ограниченными версиями стандарта Функции Python: eval(), execfile() и операторы 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". Хотя вы можете переопределить execfile().
Обратите внимание, что этот подход охватывает только exec/execfile, а не "import".
Для подключения нагрузки модуля "более высокий уровень" вы можете использовать использование
sys.path_hooks (Запись из PyMOTW).
Это все, что у меня на голове.
Ответ 7
Это должно работать в большинстве случаев:
import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
Ответ 8
os.path...
была "сделанная вещь" в Python 2.
В Python 3 вы можете найти каталог сценариев следующим образом:
from pathlib import Path
cwd = Path(__file__).parents[0]
Ответ 9
Вот частичное решение, все еще лучше, чем все опубликованные до сих пор.
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]
не будет работать, вернет -c
, если вы выполните script с python -c "execfile('path-tester.py')"
- Я опубликовал полный тест на https://gist.github.com/1385555, и вы можете его улучшить.
Ответ 10
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:/
)
Ответ 11
Would
import os
cwd = os.getcwd()
делай, что хочешь? Я не уверен, что именно вы подразумеваете под "текущим каталогом script". Каким будет ожидаемый результат для используемых вами случаев использования?
Ответ 12
Надеюсь, это поможет:
Если вы запустите script/module из любого места, вы сможете получить доступ к переменной __file__
, которая является модульной переменной, представляющей местоположение script.
С другой стороны, если вы используете интерпретатор, у вас нет доступа к этой переменной, где вы получите имя NameError
, а os.getcwd()
предоставит вам неправильный каталог, если вы используете файл из другого места.
Это решение должно дать вам то, что вы ищете во всех случаях:
from inspect import getsourcefile
from os.path import abspath
abspath(getsourcefile(lambda:0))
Я не проверил его полностью, но решил мою проблему.
Ответ 13
только pwd
на ноутбуке jupyter: pwd
+ shift + enter
на spyder: pwd
+ F9