Python: лучший способ добавить к sys.path относительно текущего запуска script

У меня есть каталог, полный скриптов (скажем project/bin). У меня также есть библиотека, расположенная в project/lib, и хочу, чтобы скрипты автоматически загружали ее. Это то, что я обычно использую в верхней части каждого script:

#!/usr/bin/python
from os.path import dirname, realpath, sep, pardir
import sys
sys.path.append(dirname(realpath(__file__)) + sep + pardir + sep + "lib")

# ... now the real code
import mylib

Это довольно громоздко, уродливо и должно быть вставлено в начале каждого файла. Есть ли лучший способ сделать это?

Действительно, на что я надеюсь, это что-то столь же гладко, как это:

#!/usr/bin/python
import sys.path
from os.path import pardir, sep
sys.path.append_relative(pardir + sep + "lib")

import mylib

Или, что еще лучше, что-то, что не сломается, когда мой редактор (или кто-то другой, кто имеет право доступа) решает переупорядочить импорт как часть процесса очистки:

#!/usr/bin/python --relpath_append ../lib
import mylib

Это не будет напрямую переноситься на платформы, отличные от posix, но это будет чистым.

Ответ 1

Если вы не хотите редактировать каждый файл

  • Установите библиотеку как обычный python libray
    или
  • Установите PYTHONPATH на lib

или если вы хотите добавить одну строку в каждый файл, добавьте оператор импорта вверху, например.

import import_my_lib

сохранить import_my_lib.py в bin и import_my_lib может корректно установить путь python к любому lib, который вы хотите

Ответ 2

Это то, что я использую:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))

Ответ 3

Я использую:

import sys,os
sys.path.append(os.getcwd())

Ответ 4

Создайте оберточный модуль project/bin/lib, который содержит следующее:

import sys, os

sys.path.insert(0, os.path.join(
    os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib'))

import mylib

del sys.path[0], sys, os

Затем вы можете заменить весь треск в верхней части ваших скриптов:

#!/usr/bin/python
from lib import mylib

Ответ 5

Если вы не хотите каким-либо образом изменять содержимое script, добавьте текущий рабочий каталог . в $PYTHONPATH (см. пример ниже)

PYTHONPATH=.:$PYTHONPATH alembic revision --autogenerate -m "First revision"

И назовите это днем!

Ответ 6

Существует проблема с каждым ответом, который можно суммировать как "просто добавьте это магическое заклинание к началу вашего сценария. Посмотрите, что вы можете сделать только с одной строкой или двумя кодами". Они не будут работать во всех возможных ситуациях!

Например, одно из таких магических заклинаний использует файл. К сожалению, если вы упаковываете свой скрипт с помощью cx_Freeze или используете IDLE, это приведет к исключению.

В другом таком магическом заклинании используется os.getcwd(). Это будет работать, только если вы запускаете свой скрипт из командной строки, а каталог, содержащий ваш скрипт, является текущим рабочим каталогом (то есть вы использовали команду cd для перехода в каталог до запуска скрипта). Боги! Надеюсь, мне не нужно объяснять, почему это не сработает, если ваш скрипт Python находится в PATH где-то, и вы запустили его, просто набрав имя вашего файла сценария.

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

import inspect
import os
import sys

# Add script directory to sys.path.
# This is complicated due to the fact that __file__ is not always defined.

def GetScriptDirectory():
    if hasattr(GetScriptDirectory, "dir"):
        return GetScriptDirectory.dir
    module_path = ""
    try:
        # The easy way. Just use __file__.
        # Unfortunately, __file__ is not available when cx_freeze is used or in IDLE.
        module_path = __file__
    except NameError:
        if len(sys.argv) > 0 and len(sys.argv[0]) > 0 and os.path.isabs(sys.argv[0]):
            module_path = sys.argv[0]
        else:
            module_path = os.path.abspath(inspect.getfile(GetScriptDirectory))
            if not os.path.exists(module_path):
                # If cx_freeze is used the value of the module_path variable at this point is in the following format.
                # {PathToExeFile}\{NameOfPythonSourceFile}. This makes it necessary to strip off the file name to get the correct
                # path.
                module_path = os.path.dirname(module_path)
    GetScriptDirectory.dir = os.path.dirname(module_path)
    return GetScriptDirectory.dir

sys.path.append(os.path.join(GetScriptDirectory(), "lib"))
print(GetScriptDirectory())
print(sys.path)

Как вы можете видеть, это непростая задача!