Автоматически вызывать общий код инициализации без создания файла __init__.py

У меня есть два каталога в моем проекте:

project/
  src/
  scripts/

"src" содержит мой отточенный код, а "scripts" содержит одноразовые скрипты Python.

Я хотел бы, чтобы все скрипты добавляли «../src» к их sys.path, чтобы они могли получить доступ к модулям в дереве «src». Один из способов сделать это — написать файл scripts/__init__.py с содержимым:

scripts/__init__.py:
  import sys
  sys.path.append("../src")

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

Я мог бы просто отредактировать переменную среды PYTHONPATH в моем .bashrc, но я хочу, чтобы мои сценарии работали «из коробки», не требуя от пользователя возиться с PYTHONPATH. Кроме того, мне не нравится вносить изменения во всю учетную запись только для того, чтобы приспособить этот проект.


person SuperElectric    schedule 30.08.2011    source источник
comment
Не могли бы вы уточнить, что означает одноразовый? Предназначены ли сценарии для доступа к конечному пользователю после установки вашего приложения или это просто удобные инструменты только для разработки/тестирования?   -  person Greg Haskins    schedule 31.08.2011
comment
Это работает, но имеет нежелательный побочный эффект помещения всех моих скриптов в модуль под названием scripts. Не могли бы вы объяснить, почему это проблема? вы можете избежать эффекта пространства имен, взломав __init__.py, но похоже, что вы боретесь с языком (что плохо :-))   -  person Simon Bergot    schedule 31.08.2011
comment
@Simon: похоже, у тебя есть ответ. Хотите объяснить, что мне нужно сделать, чтобы взломать __init__.py? :)   -  person SuperElectric    schedule 31.08.2011


Ответы (3)


Даже если у вас есть другие планы распространения, возможно, стоит составить базовый setup.py в ваша папка src. Таким образом, вы можете запустить setup.py develop, чтобы distutils поместил ссылку на ваш код в путь по умолчанию (это означает, что любые внесенные вами изменения будут отражены на месте без необходимости «переустанавливать», и все модули будут «просто работайте», независимо от того, где находятся ваши скрипты). Это будет одноразовый шаг, но это все же еще один шаг, чем ноль, поэтому все зависит от того, будет ли это более проблематичным, чем обновление .bashrc. Если вы используете pip, эквивалентом будет pip install -e /path/to/src.

Более надежное решение, особенно если вы собираетесь зеркалировать/версионировать эти скрипты на машинах нескольких разработчиков, — это выполнять работу по разработке в контролируемой виртуальной среде. Оказывается, в virtualenv даже есть встроенная поддержка создание собственных настроек начальной загрузки. Похоже, вам просто нужен хук after_install(), чтобы настроить sitecustomize, запустить pip install -e или добавить простой файл .pth в пакеты сайтов. Пользовательский загрузчик может находиться в системе управления версиями вместе с другими скриптами, и его нужно будет запускать один раз для каждой настройки разработчика. У вас также будут обычные преимущества использования virtualenv (явное управление версиями зависимостей, изоляция от общесистемной конфигурации и стандартизация между разрозненными машинами, и это лишь некоторые из них).

Если вы действительно не хотите выполнять какие-либо шаги по настройке и готовы только запускать эти сценарии из каталога 'project', тогда вы можете __init__.py как таковой:

project/
    src/
        some_module.py
    scripts/
        __init__.py # special "magic"
        some_script.py

И вот как могут выглядеть ваши файлы:

# file: project/src/some_module.py
print("importing %r" % __name__)

def some_function():
    print("called some_function() inside %s" % __name__)
--------------------------------------------------------
# file: project/scripts/some_script.py
import some_module

if __name__ == '__main__':
    some_module.some_function()
--------------------------------------------------------
# file: project/scripts/__init__.py
import sys
from os.path import dirname, abspath, join

print("doing magic!")
sys.path.insert(0, join(dirname(dirname(abspath(__file__))), 'src'))

Тогда вам нужно будет запускать свои скрипты так:

[~/project] $ python -m scripts.some_script
doing magic!
importing 'some_module'
called some_function() inside some_module

Остерегаться! Скрипты могут только вызываться изнутри project/ следующим образом:

[~/otherdir] $ python -m scripts.some_script
ImportError: no module named scripts

Чтобы включить это, вы вернетесь к редактированию .bashrc или воспользуетесь одним из вариантов выше. Последний вариант действительно должен быть последним средством; как сказал @Simon, в этот момент вы действительно боретесь с языком.

person Greg Haskins    schedule 31.08.2011

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

Что-то звучит странно в том, что вы пытаетесь сделать. Можете ли вы показать нам пример того, чего именно вы пытаетесь достичь?

person Falmarri    schedule 30.08.2011

вы можете добавить файл с именем «pathHack.py» в каталог проекта, поместив в него что-то вроде этого:

import os
import sys
pkgDir = os.path.dirname(__file__)
sys.path.insert(os.path.join(pkgDir, 'scripts')

затем в файле python в каталоге вашего проекта начните с:

import pathHack

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

person Simon Bergot    schedule 31.08.2011