Правильная инициализация sys.path, когда основной скрипт находится в подмодуле

Первая запись sys.path — это каталог текущего скрипта, согласно документам. . В следующей настройке я хотел бы изменить это значение по умолчанию. Представьте себе следующую структуру каталогов:

src/
    core/
        stuff/
        tools/
            tool1.py
            tool2.py
    gui/
        morestuff/
        gui.py

Скрипты tool*.py и gui.py предназначены для запуска как скрипты, например:

python src/core/tools/tool2.py
python src/gui/gui.py

Теперь все инструменты импортируются из src.core.stuff, а для графического интерфейса нужен gui.morestuff. Это означает, что sys.path[0] должно указывать на src/, но по умолчанию оно указывает на src/core/tools/ или src/gui/.

Я могу настроить sys.path[0] в каждом скрипте (с такой конструкцией, например, в начале gui.py):

if __name__ == '__main__':
    if sys.path[0]: sys.path[0] = os.path.dirname(os.path.abspath(sys.path[0]))

Однако это несколько избыточно и становится утомительным для зрелой кодовой базы с тысячами скриптов. Я также знаю переключатель -m:

python -m gui.gui

Но для этого требуется, чтобы текущий каталог был src/.

Есть ли лучший способ достичь желаемого результата, например. путем изменения файлов __init__.py?

EDIT: это для Python 2.7:

~$ python -V
Python 2.7.3

person krlmlr    schedule 03.04.2013    source источник
comment
Для начала есть python -m core.tools.tool1   -  person Kos    schedule 04.04.2013
comment
Какую версию питона вы используете?   -  person filmor    schedule 04.04.2013
comment
@Kos: Спасибо, что прочитали весь пост.   -  person krlmlr    schedule 04.04.2013
comment
@krlmlr (моет очки) :)   -  person Kos    schedule 04.04.2013
comment
вы можете использовать setup.py для определения entry_points, например, entry_points={'console_scripts': ['my-tool=my.core.tools.tool2:main', 'my-gui=my.gui.gui:main']} (при условии, что пакет верхнего уровня называется my и функциональность находится в main() функциях). Он определяет сценарии my-tool и my-gui.   -  person jfs    schedule 17.04.2013
comment
см. также ссылки в этом ответе   -  person jfs    schedule 17.04.2013


Ответы (3)


Здесь у вас есть три основных варианта. Я прошел через все три как в производственной среде, так и в личных проектах. Во многом они строятся друг на друге. Тем не менее, мой совет - просто перейти к последнему.

Основная проблема заключается в том, что вам нужно, чтобы ваш каталог ./src находился в пути поиска python. Это действительно то, что касается упаковки Python.

ПИТОН ПУТЬ

Самый простой, определяемый пользователем способ настроить путь к Python — через переменную среды PYTHONPATH. Вы можете установить его во время выполнения, выполнив что-то вроде:

PYTHONPATH=/src python src/gui/gui.py

Конечно, вы также можете настроить это в своей глобальной среде, поэтому, надеюсь, все процессы, которым это нужно, найдут правильный PYTHONPATH. Но, просто помните, вы всегда будете забывать один. Обычно в 3 часа ночи, когда наконец запускается ваша cron задача.

Пакеты сайтов

Чтобы избежать необходимости в переменной среды, вы можете включить свое программное обеспечение в существующую запись в исходном пути или найти какой-то дополнительный способ добавить новый путь поиска. Таким образом, это может означать перетаскивание содержимого вашего каталога src в /usr/lib/python2.7/site-packages или туда, где находится ваша система site-packages.

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

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

Несколько лучший подход — включить файл .pth где-нибудь в пакеты вашего сайта. Когда python встречает эти файлы, он добавляет содержимое (которое должно быть именем каталога) в путь поиска. Это позволяет избежать проблемы с необходимостью не забывать добавлять новую символическую ссылку для каждого нового подпакета.

виртуальный формат и упаковка

Лучшее решение — просто стиснуть зубы и сделать настоящую упаковку Python. Это, в сочетании с отличными инструментами, такими как virtualenv и pip, позволяет вам иметь изолированный (или полуизолированный) ) среда Python.

В virtualenv у вас будет собственный site-packages только для вашего проекта, где вы можете легко установить в него свое программное обеспечение, избегая всех проблем, связанных с более ранними решениями. virtualenv также упрощает поддержку исполняемых скриптов, чтобы среда Python, в которой он работает, была именно такой, как вы ожидаете.

Единственным недостатком является то, что вам нужно написать и поддерживать setup.py, который даст указание pip (установщику Python) включить ваше программное обеспечение в virtualenv. Содержимое будет примерно таким:

!/usr/bin/env python
# -*- coding: utf-8 -*-

from distutils.core import setup


setup(
    name='myproject',
    package_dir={'myproject': 'src'},
    scripts=['src/gui/gui.py', 'src/core/tools/tool1.py', 'src/core/tools/tool2.py']
)

Итак, чтобы настроить эту среду, она будет выглядеть примерно так:

virtualenv env
env/bin/pip install -e setup.py

Чтобы запустить ваш скрипт, вы просто должны сделать что-то вроде:

env/bin/tool1.py
person rhettg    schedule 17.04.2013
comment
Спасибо за ваш очень подробный ответ. К сожалению, упаковка не вариант, программное обеспечение находится в стадии разработки в рабочей копии SVN. Однако мне было интересно, могу ли я также использовать второй подход с моей рабочей копией SVN в связи с virtualenv: поместите файл .pth в подходящем формате в подкаталог lib/python2.X/site-packages виртуальной среды. Есть предположения? - person krlmlr; 17.04.2013
comment
Разве третий вариант не обеспечит то, что вам нужно? Установка пакета с помощью pip install -e устанавливает пакет с его сценариями, связывая их с вашим кодом разработки (а не с установкой копии), поэтому любые изменения кода в репозитории будут автоматически доступны в virtualenv. - person Josh Bode; 17.04.2013
comment
Размещение соответствующего файла .pth в virtualenv - это, по сути, то, что pip install -e делает для вас (согласно ответу Джоша). Он также записывает заполнители в ваш каталог bin, которые связывают его с соответствующим экземпляром python. - person rhettg; 17.04.2013
comment
Попробую третий вариант. Большое спасибо! - person krlmlr; 18.04.2013

Единственный официально утвержденный способ запуска скрипта, находящегося в пакете, – это использование флага -m. Хотя вы можете запустить сценарий напрямую и попытаться самостоятельно выполнить sys.path манипуляции в каждом сценарии, это, вероятно, будет большой проблемой. Если вы перемещаете сценарий между папками, логика перезаписи sys.path может также нуждаться в изменении, чтобы отразить новое местоположение. Даже если вы сделаете sys.path правильно, явный относительный импорт не будет работать корректно.

Теперь для работы python -m mypackage.mymodule требуется, чтобы либо вы находились в папке верхнего уровня проекта (src в вашем случае), либо чтобы эта папка верхнего уровня находилась на пути поиска Python. Требовать, чтобы вы находились в определенной папке, неудобно, и вы сказали, что не хотите этого. Тогда наша цель — вывести src на путь поиска.

Я думаю, что лучший подход — использовать переменную среды PYTHONPATH, чтобы указать интерпретатору папку src вашего проекта, чтобы он мог найти ваши пакеты из любого места.

Это решение просто настроить (переменная окружения может быть установлена ​​автоматически в вашем .profile, .bashrc или другом эквивалентном месте) и будет работать для любого количества скриптов. Если вы переместите свой проект, просто обновите настройки среды, и все будет готово без необходимости выполнять дополнительную работу для каждого сценария.

person Blckknght    schedule 04.04.2013
comment
Спасибо за Ваш ответ. Я хотел сделать это, чтобы не устанавливать PYTHONPATH в первую очередь... - person krlmlr; 11.04.2013

Я хотел сделать это, чтобы не устанавливать PYTHONPATH в первую очередь

Есть и другие места, где вы можете подключиться к инициализации Python sys.path, используя модуль site, который (по умолчанию) автоматически импортируется при инициализации Python.

На основе этого кода в site.py...

# Prefixes for site-packages; add additional prefixes like /usr/local here
PREFIXES = [sys.prefix, sys.exec_prefix]

... похоже, что намерение состояло в том, чтобы этот файл был разработан для изменения после установки, что является одним из вариантов, хотя он также предоставляет другие способы влияния на sys.path, например. поместив файл .pth где-нибудь в вашем каталоге site-packages.

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

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

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

~$ dpkg -L python-imaging | grep pth
/usr/share/pyshared/PIL.pth
/usr/lib/python2.7/dist-packages/PIL.pth

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

Может быть и другой вариант, но он зависит от того, чего именно вы пытаетесь достичь.

person Aya    schedule 16.04.2013