Как правильно определить текущий каталог 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