2016-10-12 5 views
5

Я экспериментирую с помещением кода Python в стандартную структуру каталогов, используемую для развертывания с setup.py и, возможно, PyPI. для библиотеки Python под названием MyLib было бы что-то вроде этого:Вызов сценария в стандартной структуре каталога проекта (Python path for bin subdirectory)

mylibsrc/ 
    README.rst 
    setup.py 
    bin/ 
    some_script.py 
    mylib/ 
    __init.py__ 
    foo.py 

Там часто также подкаталог test/, но я не пробовал писать юнит-тесты еще. Рекомендация о наличии сценариев в подкаталоге bin/ может быть найдена в official Python packaging documentation.

Конечно, скрипты начинаются с кодом, который выглядит следующим образом:

#!/usr/bin/env python 
from mylib.foo import something 
something("bar") 

Это хорошо работает, когда он в конце концов приходит к развертыванию сценария (например, для devpi), а затем установить его с пипом. Но если я запускаю сценарий непосредственно из исходного каталога, как я бы при разработке новых изменений в библиотеке/сценарий, я получаю эту ошибку:

ImportError: No module named 'mylib' 

Это верно, даже если текущий рабочий каталог является корневым mylibsrc/ и я запустил скрипт, набрав ./bin/some_script.py. Это связано с тем, что Python начинает поиск пакетов в каталоге запускаемого сценария (т. Е. От bin/), а не в текущем рабочем каталоге.

Что такое хороший способ, позволяющий легко запускать скрипты при разработке пакетов?

Вот relevant other question (особенно комментарии к первому ответу).

Решения для этого, что я нашел до сих пор делятся на три категории, но ни один из них не являются идеальными:

  1. вручную подправить свой путь поиска модулей Python, как-то перед запуском скриптов.
    • Вы можете добавить mylibsrc в мою переменную окружения PYTHONPATH. Это, по-видимому, самое официальное решение (Pythonic?), Но означает, что каждый раз, когда я просматриваю проект, я должен помнить о том, чтобы вручную изменить свою среду, прежде чем я смогу запустить в ней какой-либо код.
    • Добавить . в начало моей переменной PYTHONPATH. Насколько я понимаю, это может иметь некоторые проблемы с безопасностью. На самом деле это был бы мой любимый трюк, если бы я был единственным человеком, который использовал бы мой код, но я не, и я не хочу просить других об этом.
    • Рассматривая ответы в Интернете, для файлов в каталоге test/, я видел рекомендации, что все они (косвенно) содержат строку кода sys.path.insert(0, os.path.abspath('..')) (например, в structuring your project). Тьфу! Это похоже на переносимый взлом для файлов, которые предназначены только для тестирования, но не для тех, которые будут установлены вместе с пакетом.
    • Редактировать: С тех пор я нашел альтернативу, которая оказалась в этой категории: путем запуска скриптов с помощью скрипта Python -m путь поиска начинается в рабочем каталоге, а не в каталоге bin/. См. Мой ответ ниже для более подробной информации.
  2. Установите пакет в виртуальную среду перед его использованием, используя setup.py (либо запуская его напрямую, либо используя pip).
    • Это кажется излишним, если я просто тестирую изменение, которое я не уверен, даже синтаксически корректно. Некоторые из проектов, над которыми я работаю, даже не предназначены для установки в виде пакетов, но я хочу использовать одну и ту же структуру каталогов для всего, и это будет означать запись setup.py, чтобы я мог их протестировать!
    • Редактировать: Два интересных варианта этого обсуждаются в ответах ниже: команда setup.py develop в ответе logc и pip install -e в моем. Они избегают необходимости «устанавливать» для каждого небольшого редактирования, но вам все равно нужно создать setup.py для пакетов, которые вы никогда не собираетесь полностью устанавливать, и не очень хорошо работает с PyCharm (у которого есть запись меню для запуска develop но нет простого способа запуска скриптов, которые он копирует в виртуальную среду).
  3. Переместите сценарии в корневой каталог проекта (то есть в mylibsrc/ вместо mylibsrc/bin/).
    • Yuck! Это последнее средство , но, к сожалению, это похоже на единственный возможный вариант на данный момент .

ответ

2

Запуск модулей в качестве сценариев

Поскольку я отвечал на этот вопрос, я узнал, что вы можете запустить модуль, как если это был скрипт с использованием Python -m командной строки (который, как я думал, применим только к пакетам).

Так что я думаю, что лучшее решение заключается в следующем:

  • Вместо написания оберток скриптов в подкаталоге BIN, положить большую часть логики в модулях (как вы должны в любом случае), и поставить в конце соответствующие модули if __name__ == "__main__": main(), как и в скрипте.
  • Для запуска сценариев в командной строке, вызовите модули непосредственно следующим образом: python -m pkg_name.module_name
  • Если у вас есть setup.py, а Алик сказал, что вы можете создавать сценарии оболочки во время установки, чтобы пользователи не нужен запускайте их этим забавным способом.

PyCharm не поддерживает запуск модулей таким образом (см. this request). Однако вы можете просто запускать модули (а также скрипты в bin), как обычно, потому что PyCharm автоматически добавляет корень проекта в PYTHONPATH, поэтому операции импорта разрешаются без каких-либо дополнительных усилий. Для этого есть несколько ошибок:

  • Основная проблема: рабочий каталог будет неправильным, поэтому открытие файлов данных не будет работать. К сожалению, быстрого решения нет. при первом запуске каждого сценария вы должны остановить его и изменить настроенный рабочий каталог (см. this link).
  • Если каталог вашего пакета не находится непосредственно в корневом каталоге проекта, вам необходимо пометить его родительский каталог как исходный каталог на странице настроек структуры проекта.
  • Относительный импорт не работает, то есть вы можете сделать from pkg_name.other_module import fn, но не from .other_module import fn. В любом случае относительный импорт, как правило, плохой стиль, но они полезны для модульных тестов.
  • Если модуль имеет круговую зависимость и вы запускаете его напрямую, он будет дважды импортирован (один раз как pkg_name.module_name и один раз как __main__). Но в любом случае вы не должны иметь круговых зависимостей.

Bonus командной строки весело:

  • Если вы все еще хотите поставить некоторые скрипты в bin/ вы можете называть их с python -m bin.scriptname (но в Python 2 вам нужно положить __init__.py в каталоге бен).
  • Вы можете даже запустить общий пакет, если он имеет __main__.py, как это: python -m pkg_name

Пип редактируемые режим

Существует альтернатива для командной строки, который не так просто, но до сих пор стоит знать: редактируемый режим

  • используйте Пипа, documented here
  • Чтобы использовать его, сделать setup.py и использовать следующую команду т o установите пакет в свою виртуальную среду: pip install -e .
  • Обратите внимание на конечную точку, которая ссылается на текущий каталог.
  • Это ставит скрипты, сгенерированные с вашего setup.py в каталог bin вашей виртуальной среды, и ссылается на исходный код вашего пакета, поэтому вы можете редактировать и отлаживать его без повторного запуска pip.
  • Когда вы закончите, вы можете запустить pip uninstall pkg_name
  • Это похоже на develop команды setup.py «s, но удаление, кажется, работает лучше.
1

Самый простой способ заключается в использовании setuptools в вашем setup.py сценарии и использовать entry_points ключевое слово, смотрите документацию Automatic Script Creation.

Более подробно: вы создаете setup.py, который выглядит как этот

from setuptools import setup 

setup(
    # other arguments here... 
    entry_points={ 
     'console_scripts': [ 
      'foo = my_package.some_module:main_func', 
      'bar = other_module:some_func', 
     ], 
     'gui_scripts': [ 
      'baz = my_package_gui:start_func', 
     ] 
    } 
) 

затем создать другие пакеты Python и модули под каталог, где этот setup.py существует, например, Ниже приведенном выше примере:

. 
├── my_package 
│   ├── __init__.py 
│   └── some_module.py 
├── my_package_gui 
│   └── __init__.py 
├── other_module.py 
└── setup.py 

, а затем запустить

$ python setup.py install 

или

$ python setup.py develop 

В любом случае, новые скрипты Python (исполняемые скрипты без суффикса .py) созданы для вас, что точка к точкам входа, которые вы описали в setup.py. Обычно они относятся к понятию интерпретатора Python «каталог, где должны быть исполняемые двоичные файлы», который обычно находится на вашем PATH. Если вы используете виртуальный env, то virtualenv обманывает интерпретатор Python, думая, что этот каталог находится в bin/, где бы вы ни определили, что должен быть virtualenv. Следуя примеру выше, в virtualenv, выполнив предыдущие команды должны привести к:

bin 
├── bar 
├── baz 
└── foo 
+0

Спасибо за подробный ответ. Материал о «entry_points» интересен, но не хватает всего: я не хочу устанавливать пакет, чтобы попробовать все крошечные экспериментальные изменения (на самом деле это делает это хуже!). Но вы упомянули мимоходом самородок, который является реальным решением: команда 'setup.py develop'. Это будет работать как с «entry_points», так и с обычными скриптами. Жаль, что он требует, чтобы вы создавали 'setup.py' даже для пакетов, которые вы никогда не намерены правильно устанавливать, и нелегко получить доступ из среды IDE, такой как PyCharm. –

 Смежные вопросы

  • Нет связанных вопросов^_^