Создать свой PyPI репозиторий для рабочего проекта мне пришлось сразу по нескольким причинам: начиная от архитектурных особенностей, заканчивая простотой удобством такого решения. Началось все с того, что наше приложение состоит из набора независимых между собой частей, packages. Соответственно, каждый такой пакет может зависить от любого числа других - как самописных так и нет. В таком случае создавать для каждого пакета свой файл pip-requirements для разворачивания окружения стало сложно и неудобно. Следующая причина - это сделать возможность установки любого пакета нужной верисии не имея доступа к нашей системе контроля версий (mercurial). Все-таки набрать команду “pip install my-package-name” намного проще и быстрее, чем указывать путь к исходникам или source control. И последняя причина для меня была в том, что необходимо следить за всеми зависимостями к внешним библиотекам: не только использовать нужную версию, устанавливать пропатченную, но и иметь список всего используемого в проекте (с этим пунктом возникло больше всего трудностей).
И так, что же представляет собой любой PyPI репозиторий? В простейшем случае - это папка с пакетами. Но для более легкого использования все же удобнее использовать веб-сервер. Так же можно использовать любой, но есть и специально написанные для этого приложения, которые имеют ряд примуществ. Я для себя пока остановился на pypiserver (http://pypi.python.org/pypi/pypiserver). Он достаточно простой, но при этом может работать как stand alone приложение или wsgi приложение с разными веб-серверами. Из недостатков, которые сразу бросились в глаза - это лишь то, что он не поддерживает upload пактов, т.е. команда “python setup.py upload” с ним работать не будет, но это автор обещает исправить в следующих версиях.
Установка pypiserver, самом простом случае выглядит так:
- $ pip install pypiserver
- $ mkdir ~/packages
- копируем нужные пакеты в папку ~/packages и запускаем сервер:
- $ pypi-server -p 8080 ~/packages
После чего репозиторий будет доступен по ссылке http://localhost:8080/simple/.
Теперь собственно про создание своих пакетов. С этим хорошо справляется ditsutils, которые идут вместе с Python (к сожалению, не так хорошо, как это будет в Python 3.3).
Для начала создаем в нашем пакете файл setup.py. В простейшем случае он будет выглядеть примерно так:
setup(name=‘package-name’, version=‘0.2.1’, description=‘description’, author=‘e0ne’, author_email='e0ne@e0ne.info, url=‘http://blog.e0ne.info’, packages=[‘package_name’, ], )
Далее создаем дистрибутив нашего пакета. В моем случае это только питоновские модули, поэтому меня вполне устроил sources distrib:
**$ python setup.py bdist **
Эта команда создаст каталог dist и положит туда архив с дистрибудивом пакета, который, в моем случае, нужно положить в ~/packages. После чего остается лишь выполнить команду для его установки:
$ pip install package-name -i http://localhost:8080/simple
Для более простого использования можно переопределить переменную окружения PIP_INDEX_URL, чтобы еще упростить вызов команды:
$ export PIP_INDEX_URL=http://localhost:8080/simple/$ pip install package-name
Следующим шагом что нужно сделать - это автоматизировать процесс создангия и выкладывания на свой PyPI-сервер пакетов путем интеграции с continuous integration (CI) сервером. Но так, как это сильно зависит от проекта, выбора CI и структыры исходного кода - приводить пример сдесь не буду. Кому нужно сможет сделать это сам или задать нужные вопросы в комментариях.
И последняя проблема у меня заключалась в том, что нужно полностью отказаться от http://pypi.python.org/pypi по двум причинам: ускорить процесс сборки билда путем удаления необходимости выкачивать нужные зависимости с интернета и иметь полный список всех зависимостей. При этом делать полное зеркало не хотелось. Как оказалось, в Python 2.7 сделать это не так уж и просто (на досуге нужно будет посмотреть как там с этим в Python 3.3). Проблема в том, что pip installer, который у нас используется устанавливает пакеты с помощью стандартных setuptools, что практически аналогично вызову команды “python setup.py install”. И если в setup.py есть строки типа install_requires=’django’, то setuptools не найдет это в локальном репо полезет в интернет. Заставить его работать по-другому без фикса кода можно несколькими способами. Самые простые - это:
- добавить запись в /etc/hosts вида “127.0.0.1 pypi.python.org”
- настроить прокси-сервер для блокирования доступа к pypi.python.org с билд-машины.
Методы кардинальные, но работают. При этом поломанные билды из-за отсутствия нужного пакета в локальном репозиории лежат полностью на совести разработчиков.
Ссылки по теме:
- http://pypi.python.org/pypi/pypiserver
- http://www.pip-installer.org/en/latest/index.html
- http://docs.python.org/distutils/index.html
- http://aboutsimon.com/2012/02/24/create-a-local-pypi-mirror/