Python: лучший способ добавить в sys.path относительно текущего запущенного скрипта - PullRequest
76 голосов
/ 29 декабря 2011

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

#!/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

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

Ответы [ 8 ]

97 голосов
/ 29 декабря 2011

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

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
24 голосов
/ 24 февраля 2017

Я использую:

import sys,os
sys.path.append(os.getcwd())
20 голосов
/ 29 декабря 2011

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

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

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

import import_my_lib

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

11 голосов
/ 29 декабря 2011

Создайте модуль-обертку 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
6 голосов
/ 15 марта 2016

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

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

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

4 голосов
/ 08 сентября 2018

Использование python 3.4 +
Запрет использования cx_freeze или использования в режиме IDLE. ?

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")
2 голосов
/ 25 апреля 2018

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

Например, одно из таких магических заклинаний использует file .К сожалению, если вы упакуете свой скрипт с помощью 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)

Как видите, это нелегкая задача!

0 голосов
/ 28 мая 2019

Вы можете запустить скрипт с python -m из соответствующего корневого каталога.И передайте «путь модулей» в качестве аргумента.

Пример: $ python -m module.sub_module.main # Notice there is no '.py' at the end.


Другой пример:

$ tree  # Given this file structure
.
├── bar
│   ├── __init__.py
│   └── mod.py
└── foo
    ├── __init__.py
    └── main.py

$ cat foo/main.py
from bar.mod import print1
print1()

$ cat bar/mod.py
def print1():
    print('In bar/mod.py')

$ python foo/main.py  # This gives an error
Traceback (most recent call last):
  File "foo/main.py", line 1, in <module>
    from bar.mod import print1
ImportError: No module named bar.mod

$ python -m foo.main  # But this succeeds
In bar/mod.py
...