Разработка пакетов python с помощью SVN и buildout
Подготовка к разработке
Устанавливаем в папку где будут создаваться пакеты - Python 2.5.2 + PIL (по зависимостям может не собраться)
В домашней директории создаем директорию .buildout и в ней файл default.cfg в который вписываем
[buildout] newest = false (1) eggs-directory = /Users/User/Project/egg (2) find-links = http://download.zope.org/ppix (3)
1. Обновление запрещено
2. Директория куда будут складываться egg
3. Где их искать
Для удобства в папке с пакетами можно сделать симулинк на Python
Использование egg и buildout
Разрабатываемые пакеты имеют следующую структуру:
Допустим разрабатываемый пакет test1 в проекте test.
test.test1/
branches/
tags/
trunk/ (1) Readme.txt (2) bootstrap.py (3) setup.py (4) buildout.cfg (5) deploy.cfg (6) src/ (7) test/ (8) __init__.py (9) test1/ (10) i18n.py (11) ... test1.txt (12) ... tests/ (13) __init__.py test1.py (14)
Пакет имеет начальную структуру для помещения в репозиторий.
1 - Директория где находится пакет при разработке.
2 - Описание пакета
3 - Файл инициализации пакета, первый раз скачивается в любую директорию
svn co svn://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap/bootstrap.py
и помещается в проект, в дальнейшем достаточно копировать этот файл в новый проект.
4 - Файл для создания egg. (подробнее раздел setup.py)
5 - Файл для сборки пакета в отладочном режиме. (подробнее в разделе cfg)
6 - Файл для сборки пакета в нормальном режиме. (подробнее в разделе cfg)
7 - Каталог с исходниками пакета.
8 - Название проекта.
9 - Специальный файл для возможности импорта пакета (копируется из любого пакета где он есть).
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages try: __import__('pkg_resources').declare_namespace(__name__) except ImportError: from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
11 - Файл с регистрацией перевода.
#-*- coding: utf-8 -*- import zope.i18nmessageid MessageFactory = _ = zope.i18nmessageid.MessageFactory("test.test1")
12 - Тесты. (подробнее в разделе тестирование)
13 - Каталог с исходниками для запуска теста. (подробнее в разделе тестирование)
14 - Файл вызова теста. (подробнее в разделе тестирование)
setup.py
from setuptools import setup, find_packages
setup( name='test.test1', # Название пакета version='0.1', # Версия packages=find_packages('src'), # Где искать исходники [1] package_dir={'': 'src'}, # Директория с пакетом package_data = {'': ['*.txt','*.pt','*.zcml'],}, # Что паковать кроме py файлов [2] namespace_packages = ['test'], # Неймспейс [3] install_requires=['setuptools', # Всегда необходима ... # Зависимости [4] ], dependency_links = ['http://download.zope.org/distribution'], # Где искать зависимости zip_safe=False, # Не сжимаем )
1 - Исходником считается дириктория с файлом init.py
2 - Для директорий где нет файла init.py необходимо указать явно что паковать, например:
package_data = {'': ['*.txt','*.pt','*.zcml','resources/*.*','resources/lang/*.js'],},
если пакет с большим количеством таких директорий то можно получить все директории командой :
find . -type d
3 - Регистрируем неймспейс для возможности импорта
4 - То от чего зависит наш проект. Для получения зависимостей можно использовать команду:
cat `find test.test1/trunk/src/ -name "*.py"` |grep import
и вручную отредактировать.
Дополнительно можно почитать здесь: http://peak.telecommunity.com/DevCenter/setuptools
cfg
Если нет зависимостей от других частей нашего проекта то buildout.cfg deploy.cfg выглядят так:
[buildout] develop = . parts = test i18n scripts [test] recipe = zc.recipe.testrunner eggs = test.test1 [i18n] recipe = z3c.recipe.i18n:i18n packages = test.test1 eggs = test.test1 domain = test.test1 output = src/test/test1/locales zcml = [scripts] recipe = zc.recipe.egg eggs = test.test1 interpreter = py
Собирается пакет следующим образом: /patch/to/python/python2.5 bootstrap.py
после этой команды сгенерируется скрипт установщика bin/buildout при запуске которого сгенерируются разделы указанные в файле cfg.
Для раздела test: bin/test который запускает тесты написанные в пакете
Для раздела i18n: 3 скрипта bin/i18nstats bin/i18nmergeall bin/i18nextract для генерации переводов
Для раздела scripts: bin/py интерпритатор python где доступен пакет. Полезно проделать тесты в нем и потом скопировать их в текстовый файл с тестами.
Подробнее: http://pypi.python.org/pypi/zc.buildout
Загрузка на сервер
После сборки нужно экспортировать проект в репозиторий, предварительно очистив его от созданных при сборке файлов, можно так:
переходим в наш проект/trunk и выполняем
rm -rf parts; rm -rf dist; rm -rf develop-eggs; rm -rf build; rm -rf bin; rm -rf src/*.egg-info; rm .installed.cfg;
после этого переходим в папку где находится наш проект и импортируем его в репозиторий:
svn import test.test1 https://xx.xx.xx.xx/svn/zope3/test.test1 -m 'Init'
можно создавать рабочую копию предварительно удалив проект
rm -rf test.test1/ svn co https://xx.xx.xx.xx/svn/zope3/test.test1/trunk test.test1
теперь для удобства отладки нужно установить свойства svn для нашего проекта, чтобы создаваемые при сборке файлы и директории не просились в репозиторий, можно выполнить в каталоге с проектом svn propedit . и появившемся редакторе вписать:
bin build dist develop-eggs parts .installed.cfg
после чего svn propedit src и вписать
*.egg-info
но я предпочитаю создать 2 файла в каталоге с проектом в которых вписаны соответствующие свойства и устанавливать свойства так:
svn propset svn:ignore . -F ../svn_ignore.txt svn propset svn:ignore src -F ../svn_ignore2.txt
после чего все это закомитить: svn commit -m 'set ignore';
После того как проект будет полностью отлажен, можно загрузить его на egg сервер:
bin/buildout setup setup.py bdist_egg upload -r http://192.168.0.6:8100/eggs
для загрузки с конфигурацией deploy.cfg нужно указать ключ -с и имя файла deploy.cfg.
Зависимости от наших пакетов
Если разрабатываемый пакет зависит от ранее разработанных пакетов то нужно учесть несколько моментов:
Файлы buildout.cfg и deploy.cfg различаются тем как будут браться зависимые пакеты, в 1м случае при отладке зависимые пакеты берутся из папки externals которая помещается в каталог trunk во 2м берутся с нашего egg сервера, для чего он прописывается в конфигурацию deploy.cfg:
find-links = http://host:8000/eggs/simple
Настройка папки externals:
после создания этой папки необходимо добавить ее в репозиторий, подразумевается что проект добавлен в репозиторий.
Допустим проект test1 завист от проекта test2. После добавления нужно настроить свойства svn для папки externals: в каталоге проекта выполнить svn propedit svn:externals, в появившемся редакторе вписать:
test.test2 https://xx.xx.xx.xx/svn/zope3/test.test2/trunk
или вписать это в файл и выполнить
svn propset svn:externals externals -F ../ext.txt
теперь можно сделать комит и обновиться svn up, после обновления в каталоге externals создастся рабочая копия зависимого пакета. Для того чтобы при сборке можно было импортировать зависимый пакет в файле buildout.cfg в разделе develop вписывается путь к зависимому пакету:
develop = externals/test.test2 .
и указывается в зависимостях в файле setup.py. Если зависимых пакетов несколько, то они вписываются через пробел, в конце стоит точка, что означает текущий каталог. Если внутри зависимого пакета существуют такие же пакеты, необходимо указать из расположение в разрабатываемом пакета, например пакет test2 зависит от пакета test3:
develop = externals/test.test2 externals/test.test2/externals/test.test3 .
Важно: Перед удалением папки externals нужно убрать установки svn:
1 - svn propdel svn:externals externals 2 - svs commit -m “del prop” 3 - удаляем содержимое externals 4 - svn del externals
Иначе можно удалить зависимый пакет из репозитория
Тестирование
При сборке пакета устанавливается интерпритатор питона bin/py который можно использовать для тестов
В разрабатываемом пакете в каталоге tests создается питоновский файл для тестирования:
import unittest from zope.testing import doctest
def test_suite(): return unittest.TestSuite(( doctest.DocFileSuite("../test1.txt"), ))
В текстовом файле test1 прописываются тесты:
Импорт и регистрация утилиты(менеджера) для генерации паролей, MD5 т.к. по умолчанию используется именно он. Данная менеджер требуется при создания экземпляра SQLPrincipal.
>>> from zope.app.authentication.interfaces import IPasswordManager
>>> from zope.app.authentication.password import MD5PasswordManager
>>> from zope.component import getGlobalSiteManager
>>> pass_manager = MD5PasswordManager() >>> gsm = getGlobalSiteManager() >>> pass_manager = MD5PasswordManager() >>> gsm.registerUtility(pass_manager, IPasswordManager, name="MD5")
Теперь импортируем все что необходимо для работы с БД(SQL) Для доступа используется sqlalchemy и zope.sqlalchemy(интерграция
с механизмом транзакций).
>>> from sqlalchemy import * >>> from sqlalchemy.orm import scoped_session, sessionmaker
>>> from zope.sqlalchemy import ZopeTransactionExtension
>>> import transaction
Каждый тест отделяется абазацем. Для тестирования запускается скрипт bin/test
Автор: Рыдиков Максим max@rydikov.com