Разработка пакетов python с помощью SVN и buildout

В статье описываются принципы использования SVN и buildout для подготовки python-пакетов.

Подготовка к разработке

Устанавливаем в папку где будут создаваться пакеты - 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

Вам также может помочь