Как правильно определить текущий каталог скриптов? - PullRequest
212 голосов
/ 15 сентября 2010

Хотелось бы узнать, как лучше определить текущий каталог скриптов в 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().

Внимание

Любое решение, использующее текущий каталог, завершится неудачей, оно может отличаться в зависимости от способа вызова скрипта или может быть изменено внутри запущенного скрипта.

Ответы [ 13 ]

190 голосов
/ 15 сентября 2010
os.path.dirname(os.path.abspath(__file__))

- действительно лучшее, что вы получите.

Необычно выполнять сценарий с exec / execfile;обычно вы должны использовать инфраструктуру модуля для загрузки скриптов.Если вы должны использовать эти методы, я предлагаю установить __file__ в globals, который вы передаете скрипту, чтобы он мог прочитать это имя файла.

Нет другого способа получить имя файла в коде execed: так как выОбратите внимание, что CWD может быть в совершенно другом месте.

117 голосов
/ 02 июня 2011

Если вы действительно хотите охватить случай, когда скрипт вызывается через execfile(...), вы можете использовать модуль inspect для определения имени файла (включая путь).Насколько мне известно, это будет работать для всех перечисленных вами случаев:

filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
40 голосов
/ 05 апреля 2014
#!/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-файла (/ яйца) .Работает, если скрипт «импортирован» (PYTHONPATH=/path/to/library.zip python -mscript_to_run) из zip-файла;в этом случае он возвращает путь к архиву.Это работает, если скрипт скомпилирован в автономный исполняемый файл (sys.frozen).Работает с символическими ссылками (realpath исключает символические ссылки).Работает в интерактивном переводчике;в этом случае он возвращает текущий рабочий каталог.

22 голосов
/ 25 мая 2017

В Python 3.4+ вы можете использовать более простой модуль pathlib:

from inspect import currentframe, getframeinfo
from pathlib import Path

filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent
6 голосов
/ 22 февраля 2018

Подход os.path... был «готовым делом» в Python 2.

В Python 3 вы можете найти каталог скриптов следующим образом:

from pathlib import Path
cwd = Path(__file__).parents[0]
5 голосов
/ 04 июня 2011

Просто используйте os.path.dirname(os.path.abspath(__file__)) и очень внимательно изучите, действительно ли существует необходимость в случае, когда используется exec. Это может быть признаком проблемного дизайна, если вы не можете использовать свой скрипт в качестве модуля.

Имейте в виду Zen of Python # 8 , и если вы считаете, что есть хороший аргумент для варианта использования, где он должен работать для exec, то, пожалуйста, сообщите нам более подробную информацию о предыстория проблемы.

3 голосов
/ 11 мая 2018
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:/)

3 голосов
/ 05 июня 2011

Во-первых ... пара пропущенных вариантов использования здесь, если мы говорим о способах внедрения анонимного кода ..

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).

Это все, что у меня есть на макушке.

3 голосов
/ 15 сентября 2010

Будет ли

import os
cwd = os.getcwd()

делать то, что вы хотите?Я не уверен, что именно вы подразумеваете под "текущим каталогом скриптов".Каким будет ожидаемый результат для приведенных вами вариантов использования?

2 голосов
/ 14 июня 2015

Это должно работать в большинстве случаев:

import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...