Оказывается celeryd загрузчик перекрывает стандартную logging конфигурацию для того чтобы облегчить жизнь разработчику.
Иинциатива конечно хорошая, однако для нас это было полной неожиданностью.
К счастью есть как минимум два способа изменить это поведение.
Быстрое, но не совсем полное решение это установить CELERYD_HIJACK_ROOT_LOGGER = False.
Чуть более сложное, но концептуально верное решение это использовать setup-logging хук/сигнал, который предоставляет Celery.
Ссылки:
In 2.1 logging behavior was changed to not configure logging if it was
already configured. The problem is that some libraries does not play
nice and hijack the root logger, or use logging.basicConfig – resulting
in users not getting any output or logs.
http://readthedocs.org/docs/celery/en/release21-maint/changelog.html#version-2-1-4
By default any previously configured logging options will be reset, because the Celery programs “hijacks” the root logger.
If you want to customize your own logging then you can disable this behavior.
http://ask.github.com/celery/configuration.html#celeryd-hijack-root-logger
setup_logging signal.
Celery won’t configure the loggers if this signal is connected, so you can use this to completely override the logging configuration with your own.
http://ask.github.com/celery/userguide/signals.html#setup-logging
Показаны сообщения с ярлыком Python. Показать все сообщения
Показаны сообщения с ярлыком Python. Показать все сообщения
вторник, 13 декабря 2011 г.
воскресенье, 16 октября 2011 г.
SQLAlchemy for Python. Some queries examples including generated SQL
UPDATE on 2011-10-18. Ответил на некоторые комментарии прямо внизу поста.
Не так давно мне посчастливилось организовать technology validation самого продвинутого ORM-фреймверка для языка Python, который называется SQLAlchemy.
Признаюсь что перед стартом этого процесса я был достаточно скептически настроен, у меня была уверенность что я не увижу функционала, который бы хотя бы немного дотягивал до Java/Hibernate или .NET/NHibernate.
На моем последнем .NET-проекте, который назывался TravelConfirm, я предложил использовать NHibernate, о чем ни разу не жалею.
Более того, рекомендую всем сомневающимся внедрять его везде, где бы вы планировали использовать Entity Framework. NHibernate в связке с Fluent NHibernate прекрасно показали себя в работе с MySQL и Amazon RDS несмотря на то, что мы использовали в том числе и хранимые процедуры.
Но давайте вернемся к SQLAlchemy.
Статья на Wikipedia гласит что SQLAlchemy это open source SQL toolkit и object relational mapper, первый релиз которого был в феврале 2006-го года и что проект лицензируется под достаточно лояльной open source лицензией MIT.
http://en.wikipedia.org/wiki/SQLAlchemy
SQLAlchemy поддерживает довольно внушительный список СУБД, который в первом приближении сопоставим с таковым у Java/Hibernate:
Актуальную информацию по поддерживаемым диалектам можно увидеть тут:
http://www.sqlalchemy.org/docs/dialects/index.html
Еще одна особенность, которую я постояно наблюдаю в стеке Python, так это огромное изобилие библиотек, функциональность которых перекрывает, а иногда даже дублирует друг друга. Это вносит некоторую дополнительную головную боль разработчику, но это "правильная вещь". В рамках SQLAlchemy это выражается в том, что диалект каждой из представленных СУБД умеет работать поверх нескольких реализаций DB-API драйверов.
Например с MySQL я могу работать используя MySQL Connector/J, MySQL Connector/Python, mysql-python, OurSQL либо pymysql.
Подробную информацию о поддерживаемых диалектах и DB-API драйверах можно увидеть по следующей ссылке:
http://www.sqlalchemy.org/docs/core/engines.html#supported-databases
Думаю что вводной информации более чем достаточно, перейдем к делу. Ниже я буду приводить набор Python-тестов, которые используют ту или иную функциональность SQLAlchemy, сразу после кода теста будет приводится SQL запрос к SQLite базе, который генерирует SQLAlchemy.
Хочу сделать акцент на том, что SQLAlchemy меня просто поразил чистотой генерируемого SQL-кода, который часто превосходит даже такого титана как NHibernate. Код приводится как есть, я только лишь расставил переносы строк и отступы для лучшей читаемости - в оригинале все эти запросы формируются в несколько длинных строк. Точно так же как это делает NHibernate.
Нативный SQL-запроса с параметрами:
Извлечение пользователей с сортировкой по трем параметрам в разных направлениях:
Извлечение пользователя, который первым попался под руку:
Извлечение всех уникальных имен пользователей:
Использование традиционных агрегирующих функций:
Извлечение с группировкой:
Надуманный вариант с подзапросом вместо традиционного join'а:
Выборка данных с тремя inner join'ами:
Выборка данных с одним jeft outer join'ом:
Ограничение выборки используя методы limit и offset из querying DSL:
Ограничение выборки используя метод slice с двумя параметрами из querying DSL. Вероятно в каком-то из диалектов метод slice может быть более эффективен:
Объединение нескольких запросов через использование union:
Объединение нескольких запросов через использование union all:
Вот примерно так выглядят типичые запросы на SQLAlchemy. К счастью мои опасения были напрасны - SQLAlchemy прекрасно справляется с задачей, когда нужно произвести выборку данных из базы.
Более того, DSL для построения запросов в SQLAlchemy даже умеет ряд вещей, которых я не видел ни в NHibernate ни в Linq, а именно:
SQLAlchemy поддерживает три основных варианта мэппинга иерархии классов аналогично NHibernate, правда они используют немного другие термины.
У SQLAlchemy термины Single Table Inheritance, Joined Table Inheritance, Concrete Table Inheritance соответствуют терминам Table per class hierarchy, Table per subclass, Table per concrete class из NHibernate:
http://www.sqlalchemy.org/docs/orm/inheritance.html
Более детально ознакомится с функциональными возможностями и их использованием можно используя официальную документацию SQLAlchemy:
http://www.sqlalchemy.org/docs/
UPDATE on 2011-10-18. Ответил на некоторые комментарии прямо внизу поста.
> Что с Foreign Keys? (mapping/использование/генерируемый SQL)
GUID. Поддержка реализована в первую очередь для PostgreSQL, но она не сложная и может быть расширена для других СУБД:
http://www.sqlalchemy.org/docs/core/types.html#backend-agnostic-guid-type
GUID.Comb не поддерживается, так что если алгоритм очень нужен, то придется его портировать с NHibernate.
HiLo генератор так же не реализован. Есть пример кастомного генератора для database shards, где говорится что его можно использовать как прототип для HiLo генератора:
https://bitbucket.org/sqlalchemy/sqlalchemy/src/54ee83285eef/examples/sharding/attribute_shard.py
Post Insert генераторы не выделены явно, однако поддерживаются по-умолчанию. Identity, Guid.Native и любой другой генератор, который вычисляется на стороне БД декларируется одинаково:
http://www.sqlalchemy.org/docs/core/schema.html#server-side-defaults
Database Sequence генератор:
http://www.sqlalchemy.org/docs/core/schema.html#defining-sequences
Composite Primary Key генератор как частный случай Assigned Key генератора:
http://stackoverflow.com/questions/2374243/is-it-possible-to-get-sqlalchemy-to-create-a-composite-primary-key-with-an-integ
К сожалению привести примеры SQL не могу, так как для этого нужно делать тесты.
> 2nd layer cache (memcached)?
SQLAlchemy умеет работать поверх Beaker Caching, который отправляет NHibernate в тяжелый нокаут по количеству поддерживаемых cache-бекендов.
С другой стороны, SQLAlchemy предоставляет более явный Caching API, что одновременно и хорошо и плохо - все зависит от личных предпочтений:
http://www.sqlalchemy.org/docs/orm/examples.html#beaker-caching
> Подзапросы в NH не так красиво как в Py, но всё же
Если быть более точным, то поздапросы в NHibernate работают только при использовании QueryOver и недоступны в Linq.
> NHibernate {bulk/batching} на вскидку 2 варианта и с недавнего (3.2) времени дэфолтный:
Bulk insert и query batching это немного разные вещи. Насколько эффективно реализован query batching в SQLAlchemy сказать не могу. Но вот bulk insert в SQLAlchemy реализован удобно, просто и не зависит от СУБД. При этом NHibernate поддерживает bulk insert только у Microsoft SQL Server'а, поскольку эта функциональность упирается в возможности нижележащего ADO.NET Data Provider'а.
Не так давно мне посчастливилось организовать technology validation самого продвинутого ORM-фреймверка для языка Python, который называется SQLAlchemy.
Признаюсь что перед стартом этого процесса я был достаточно скептически настроен, у меня была уверенность что я не увижу функционала, который бы хотя бы немного дотягивал до Java/Hibernate или .NET/NHibernate.
На моем последнем .NET-проекте, который назывался TravelConfirm, я предложил использовать NHibernate, о чем ни разу не жалею.
Более того, рекомендую всем сомневающимся внедрять его везде, где бы вы планировали использовать Entity Framework. NHibernate в связке с Fluent NHibernate прекрасно показали себя в работе с MySQL и Amazon RDS несмотря на то, что мы использовали в том числе и хранимые процедуры.
Но давайте вернемся к SQLAlchemy.
Статья на Wikipedia гласит что SQLAlchemy это open source SQL toolkit и object relational mapper, первый релиз которого был в феврале 2006-го года и что проект лицензируется под достаточно лояльной open source лицензией MIT.
http://en.wikipedia.org/wiki/SQLAlchemy
SQLAlchemy поддерживает довольно внушительный список СУБД, который в первом приближении сопоставим с таковым у Java/Hibernate:
- Drizzle
- Firebird
- IBM DB2 / Informix
- MaxDB
- Microsoft Access
- Microsoft SQL Server
- MySQL
- Oracle
- PostgreSQL
- SQLite
- Sybase
Актуальную информацию по поддерживаемым диалектам можно увидеть тут:
http://www.sqlalchemy.org/docs/dialects/index.html
Еще одна особенность, которую я постояно наблюдаю в стеке Python, так это огромное изобилие библиотек, функциональность которых перекрывает, а иногда даже дублирует друг друга. Это вносит некоторую дополнительную головную боль разработчику, но это "правильная вещь". В рамках SQLAlchemy это выражается в том, что диалект каждой из представленных СУБД умеет работать поверх нескольких реализаций DB-API драйверов.
Например с MySQL я могу работать используя MySQL Connector/J, MySQL Connector/Python, mysql-python, OurSQL либо pymysql.
Подробную информацию о поддерживаемых диалектах и DB-API драйверах можно увидеть по следующей ссылке:
http://www.sqlalchemy.org/docs/core/engines.html#supported-databases
Думаю что вводной информации более чем достаточно, перейдем к делу. Ниже я буду приводить набор Python-тестов, которые используют ту или иную функциональность SQLAlchemy, сразу после кода теста будет приводится SQL запрос к SQLite базе, который генерирует SQLAlchemy.
Хочу сделать акцент на том, что SQLAlchemy меня просто поразил чистотой генерируемого SQL-кода, который часто превосходит даже такого титана как NHibernate. Код приводится как есть, я только лишь расставил переносы строк и отступы для лучшей читаемости - в оригинале все эти запросы формируются в несколько длинных строк. Точно так же как это делает NHibernate.
Нативный SQL-запроса с параметрами:
def test_query_with_from_native_sql_with_parameters(self):
with DbSession() as session:
result = session.query(User)\
.from_statement('SELECT * FROM user where name=:name')\
.params(name='User 4')\
.all()
Generated SQL:SELECT * FROM user where name=?
Извлечение пользователей с сортировкой по трем параметрам в разных направлениях:
def test_query_with_order_by_several_columns(self): with DbSession() as session: result = session.query(User)\ .order_by(User.password)\ .order_by(User.name.desc())\ .order_by(User.age)\ .all()Generated SQL:
SELECT user.user_id AS user_user_id, user.name AS user_name, user.age AS user_age, user.password AS user_password FROM user ORDER BY user.password, user.name DESC, user.age
Извлечение пользователя, который первым попался под руку:
def test_query_with_first(self): with DbSession() as session: result = session.query(User).first()Generated SQL:
SELECT user.user_id AS user_user_id, user.name AS user_name, user.age AS user_age, user.password AS user_password FROM user LIMIT ? OFFSET ?
Извлечение всех уникальных имен пользователей:
def test_query_with_distinct(self): with DbSession() as session: result = session.query(User.name).distinct().all()Generated SQL:
SELECT DISTINCT user.name AS user_name FROM user
Использование традиционных агрегирующих функций:
def test_query_with_max_min_avg_count(self): with DbSession() as session: max_user_age = session.query(func.max(User.age)).scalar() min_user_age = session.query(func.min(User.age)).scalar() avg_user_age = session.query(func.avg(User.age)).scalar() count_user_age = session.query(func.count(User.age)).scalar()Generated SQL:
SELECT max(user.age) AS max_1 FROM user SELECT min(user.age) AS min_1 FROM user SELECT avg(user.age) AS avg_1 FROM user SELECT count(user.age) AS count_1 FROM user
Извлечение с группировкой:
def test_query_with_group_by(self):
with DbSession() as session:
result = session.query(User.name, func.count(User.user_id))\
.group_by(User.name)\
.all()
Generated SQL:SELECT user.name AS user_name, count(user.user_id) AS count_1 FROM user GROUP BY user.name
Надуманный вариант с подзапросом вместо традиционного join'а:
def test_query_with_subquery(self): with DbSession() as session: subquery = session.query(User.name).filter(User.age > 21).subquery() result = session.query(Developer).join(subquery, subquery.c.name == Developer.name).all()Generated SQL:
SELECT developer.developer_id AS developer_developer_id, developer.name AS developer_name FROM developer JOIN (SELECT user.name AS name FROM user WHERE user.age > ?) AS anon_1 ON anon_1.name = developer.name
Выборка данных с тремя inner join'ами:
def test_query_which_join_three_tables(self): with DbSession() as session: result = session.query(User, Developer, Parent)\ .join(Developer, Developer.name == User.name)\ .join(Parent, Developer.name == Parent.name)\ .all()Generated SQL:
SELECT user.user_id AS user_user_id, user.name AS user_name, user.age AS user_age, user.password AS user_password, developer.developer_id AS developer_developer_id, developer.name AS developer_name, parent.parent_id AS parent_parent_id, parent.name AS parent_name FROM user JOIN developer ON developer.name = user.name JOIN parent ON developer.name = parent.name
Выборка данных с одним jeft outer join'ом:
def test_query_with_left_outer_join(self): with DbSession() as session: result = session.query(Developer, Parent)\ .outerjoin(Parent, Parent.name == Developer.name)\ .all()Generated SQL:
SELECT developer.developer_id AS developer_developer_id, developer.name AS developer_name, parent.parent_id AS parent_parent_id, parent.name AS parent_name FROM developer LEFT OUTER JOIN parent ON parent.name = developer.name
Ограничение выборки используя методы limit и offset из querying DSL:
def test_query_with_limit_offset(self): with DbSession() as session: result = session.query(User).order_by(User.name).limit(1).offset(1).all()Generated SQL:
SELECT user.user_id AS user_user_id, user.name AS user_name, user.age AS user_age, user.password AS user_password FROM user ORDER BY user.name LIMIT ? OFFSET ?
Ограничение выборки используя метод slice с двумя параметрами из querying DSL. Вероятно в каком-то из диалектов метод slice может быть более эффективен:
def test_query_with_slice(self): with DbSession() as session: result = session.query(User).order_by(User.name).slice(start=1, stop=2).all()Generated SQL:
SELECT user.user_id AS user_user_id, user.name AS user_name, user.age AS user_age, user.password AS user_password FROM user ORDER BY user.name LIMIT ? OFFSET ?
Объединение нескольких запросов через использование union:
def test_query_with_union_selects_distinct_records(self): with DbSession() as session: query1 = session.query(User.name) query2 = session.query(Developer.name) query3 = session.query(Parent.name) result = query1.union(query2, query3).all()Generated SQL:
SELECT anon_1.user_name AS anon_1_user_name FROM (SELECT user.name AS user_name FROM user UNION SELECT developer.name AS developer_name FROM developer UNION SELECT parent.name AS parent_name FROM parent) AS anon_1
Объединение нескольких запросов через использование union all:
def test_query_with_union_all(self): with DbSession() as session: query1 = session.query(User.name,) query2 = session.query(Developer.name) query3 = session.query(Parent.name) result = query1.union_all(query2, query3).all()Generated SQL:
SELECT anon_1.user_name AS anon_1_user_name FROM (SELECT user.name AS user_name FROM user UNION ALL SELECT developer.name AS developer_name FROM developer UNION ALL SELECT parent.name AS parent_name FROM parent) AS anon_1
Вот примерно так выглядят типичые запросы на SQLAlchemy. К счастью мои опасения были напрасны - SQLAlchemy прекрасно справляется с задачей, когда нужно произвести выборку данных из базы.
Более того, DSL для построения запросов в SQLAlchemy даже умеет ряд вещей, которых я не видел ни в NHibernate ни в Linq, а именно:
- подзапросы
- предложения UNION, UNION ALL, EXCEPT, EXCEPT ALL, INTERSECT, INTERSECT ALL, HAVING, CASE
- indexing hint'ы
- bulk insert для любой СУБД (у NHibernate есть ограниченные возможности, причем работают только в Microsoft SQL Server)
- bulk update/delete. При этом database session/unit of work не трекает изменений, но не смотря на это такая функциональность бывает нужна приложению
SQLAlchemy поддерживает три основных варианта мэппинга иерархии классов аналогично NHibernate, правда они используют немного другие термины.
У SQLAlchemy термины Single Table Inheritance, Joined Table Inheritance, Concrete Table Inheritance соответствуют терминам Table per class hierarchy, Table per subclass, Table per concrete class из NHibernate:
http://www.sqlalchemy.org/docs/orm/inheritance.html
Более детально ознакомится с функциональными возможностями и их использованием можно используя официальную документацию SQLAlchemy:
http://www.sqlalchemy.org/docs/
UPDATE on 2011-10-18. Ответил на некоторые комментарии прямо внизу поста.
> Что с Foreign Keys? (mapping/использование/генерируемый SQL)
GUID. Поддержка реализована в первую очередь для PostgreSQL, но она не сложная и может быть расширена для других СУБД:
http://www.sqlalchemy.org/docs/core/types.html#backend-agnostic-guid-type
GUID.Comb не поддерживается, так что если алгоритм очень нужен, то придется его портировать с NHibernate.
HiLo генератор так же не реализован. Есть пример кастомного генератора для database shards, где говорится что его можно использовать как прототип для HiLo генератора:
https://bitbucket.org/sqlalchemy/sqlalchemy/src/54ee83285eef/examples/sharding/attribute_shard.py
Post Insert генераторы не выделены явно, однако поддерживаются по-умолчанию. Identity, Guid.Native и любой другой генератор, который вычисляется на стороне БД декларируется одинаково:
http://www.sqlalchemy.org/docs/core/schema.html#server-side-defaults
Database Sequence генератор:
http://www.sqlalchemy.org/docs/core/schema.html#defining-sequences
Composite Primary Key генератор как частный случай Assigned Key генератора:
http://stackoverflow.com/questions/2374243/is-it-possible-to-get-sqlalchemy-to-create-a-composite-primary-key-with-an-integ
К сожалению привести примеры SQL не могу, так как для этого нужно делать тесты.
> 2nd layer cache (memcached)?
SQLAlchemy умеет работать поверх Beaker Caching, который отправляет NHibernate в тяжелый нокаут по количеству поддерживаемых cache-бекендов.
С другой стороны, SQLAlchemy предоставляет более явный Caching API, что одновременно и хорошо и плохо - все зависит от личных предпочтений:
http://www.sqlalchemy.org/docs/orm/examples.html#beaker-caching
> Подзапросы в NH не так красиво как в Py, но всё же
Если быть более точным, то поздапросы в NHibernate работают только при использовании QueryOver
> NHibernate {bulk/batching} на вскидку 2 варианта и с недавнего (3.2) времени дэфолтный:
Bulk insert и query batching это немного разные вещи. Насколько эффективно реализован query batching в SQLAlchemy сказать не могу. Но вот bulk insert в SQLAlchemy реализован удобно, просто и не зависит от СУБД. При этом NHibernate поддерживает bulk insert только у Microsoft SQL Server'а, поскольку эта функциональность упирается в возможности нижележащего ADO.NET Data Provider'а.
пятница, 30 сентября 2011 г.
Кто собирал Twisted для Python под Windows 64 bit?
Фух. Сегодня выдался нелегкий денек...
Оказалось что фреймверк Twisted не собран под Windows 64. Пробовали MinGW, потом MinGW 64, потом Cygwin, потом MSVC. К сожалению пока так ничего и не получилось :(
Ознакомились с модулем logging. Нашли библиотеку которая позволит отправлять логи на нашу любимую Log2Console в формате log4j XML.
Продолжали разбираться с SQLAlchemy ORM фреймверком. Оказывается он умеет делать bulk delete/update, чего не умеет делать даже NHibernate.
В понедельник еще попробую помучать Twisted, есть еще одна идея как его собрать под Windows 64 bit...
Если удастся собрать, то тогда будет возможность посмотреть на Python logging server, собственно ради него и затеяли эту возню с Twisted :)
Оказалось что фреймверк Twisted не собран под Windows 64. Пробовали MinGW, потом MinGW 64, потом Cygwin, потом MSVC. К сожалению пока так ничего и не получилось :(
Ознакомились с модулем logging. Нашли библиотеку которая позволит отправлять логи на нашу любимую Log2Console в формате log4j XML.
Продолжали разбираться с SQLAlchemy ORM фреймверком. Оказывается он умеет делать bulk delete/update, чего не умеет делать даже NHibernate.
В понедельник еще попробую помучать Twisted, есть еще одна идея как его собрать под Windows 64 bit...
Если удастся собрать, то тогда будет возможность посмотреть на Python logging server, собственно ради него и затеяли эту возню с Twisted :)
четверг, 8 сентября 2011 г.
Погружаясь в разработку на Python...
Последнее время промышляю написанием скриптов, которые автоматиризуют различные рутиные операции:
- правка файлов AssemblyInfo.cs с целью внесения build number, git branch name, git commit hash
- компиляция Visual Studio 2010 solution с проектами на C#/.NET 4.0
- анализ кода с помощью утилиты Gendarme
- подготовка deployment архивов, в которые складываем все бинарники за исключением XML-файлов с документационными комментариями, временных файлов, лог-файлов
- сбор всех тикетов Redmine для текущего билда и внесение соответсвующего комментария в каждый из этих тикетов
Планирую еще добавить запуск NUnit тестов в связке с PartCover, подготовку Test Code Coverage отчета в HTML-формате.
Сейчас Code Coverage метрики мы вообще не собираем - никак не дойдут руки добавить, а запуск NUnit тестов для нас делает TeamCity.
Хочу прийти к тому чтобы разработчик или QA имел возможность локально запустить билд скрипт, который бы выполнил всю ту же работу, которая происходит на CI-сервере TeamCity.
Какое-то время я размышял об инструменте, который можно использовать для этих целей и в итоге остановился на CPython.
Среди кандидатов еще рассматривались Cygwin + Bash, PowerShell, IronRuby + Rake, IronPython.
Cygwin + Bach кроссплатформенные, однако язык Bash за счет своей долгой истории развития выглядит достаточно несогласованным в плане синтаксиса. На нем не очень удобно писать императивный код и я не видел чтобы кто-то использовал там дебаггер.
PowerShell напротив, отличается очень хорошей синтаксической согласованностью. У этого языка отличная интеграция с платформой Windows и .NET, что для меня как .NET разработчика неоспоримое преимущество. К тому же мне удалось найти редактор скриптов с отладчиком. Но все таки мне хотелось иметь возможность писать императивный код средней сложности, который был бы кроссплатформенным.
IronRuby + Rake можно расценивать как готовую билд систему с хорошим императивным языком. Если бы у меня не было работы с язком Pyhton, то, возможно, я бы выбрал именно этот вариант.
Python думаю не нужен в особой рекламе. Достаточно сказать что я его выбрал :) Ну а если серьезно, язык я выбрал для решения поставленных задач из-за того что он довольно популярен, решает задачи общего назначения, кросплатформенный, используется для разработки как Desktop так и Web-решений. Так же его используют для нетривиальных задач системного администрирования, которые не может "вытянуть" Bash. Наверное переломным для меня моментом была находка библиотеки Paver, которая судя по всему делает все то же, что и библиотека Rake.
На первых этапах разработка велась с использованием IronPython + paver + Visual Studio 2010 + Python Tools for Visual Studio 2010 beta, позже на CPython 2.7 + paver + concurrent.futures + lxml + Eclipse + PyDev. Сейчас присматриваюсь к среде разработки JetBrains PyCharm, хотя использование коммерческого продукта для подобных задач кажется расточительным.
По состоянию на сегодняшний день build/deplyment скрипты представляют из себя сборную солянку следующих библиотек:
- paver, BSD
- concurrent.futures, BSD
- lxml, BSD
- py-restkit, BSD
- simplejson, MIT
- http-parser, MIT
Так же я обращал внимание на следующие библиотеки:
- GitPython, BSD, работает, но не понадобилась
- PyActiveResource, MIT, не подошла из-за функциональных ограничений
- python-rest-client, GPLv3, не пробовал из-за лицензионных ограничений
- правка файлов AssemblyInfo.cs с целью внесения build number, git branch name, git commit hash
- компиляция Visual Studio 2010 solution с проектами на C#/.NET 4.0
- анализ кода с помощью утилиты Gendarme
- подготовка deployment архивов, в которые складываем все бинарники за исключением XML-файлов с документационными комментариями, временных файлов, лог-файлов
- сбор всех тикетов Redmine для текущего билда и внесение соответсвующего комментария в каждый из этих тикетов
Планирую еще добавить запуск NUnit тестов в связке с PartCover, подготовку Test Code Coverage отчета в HTML-формате.
Сейчас Code Coverage метрики мы вообще не собираем - никак не дойдут руки добавить, а запуск NUnit тестов для нас делает TeamCity.
Хочу прийти к тому чтобы разработчик или QA имел возможность локально запустить билд скрипт, который бы выполнил всю ту же работу, которая происходит на CI-сервере TeamCity.
Какое-то время я размышял об инструменте, который можно использовать для этих целей и в итоге остановился на CPython.
Среди кандидатов еще рассматривались Cygwin + Bash, PowerShell, IronRuby + Rake, IronPython.
Cygwin + Bach кроссплатформенные, однако язык Bash за счет своей долгой истории развития выглядит достаточно несогласованным в плане синтаксиса. На нем не очень удобно писать императивный код и я не видел чтобы кто-то использовал там дебаггер.
PowerShell напротив, отличается очень хорошей синтаксической согласованностью. У этого языка отличная интеграция с платформой Windows и .NET, что для меня как .NET разработчика неоспоримое преимущество. К тому же мне удалось найти редактор скриптов с отладчиком. Но все таки мне хотелось иметь возможность писать императивный код средней сложности, который был бы кроссплатформенным.
IronRuby + Rake можно расценивать как готовую билд систему с хорошим императивным языком. Если бы у меня не было работы с язком Pyhton, то, возможно, я бы выбрал именно этот вариант.
Python думаю не нужен в особой рекламе. Достаточно сказать что я его выбрал :) Ну а если серьезно, язык я выбрал для решения поставленных задач из-за того что он довольно популярен, решает задачи общего назначения, кросплатформенный, используется для разработки как Desktop так и Web-решений. Так же его используют для нетривиальных задач системного администрирования, которые не может "вытянуть" Bash. Наверное переломным для меня моментом была находка библиотеки Paver, которая судя по всему делает все то же, что и библиотека Rake.
На первых этапах разработка велась с использованием IronPython + paver + Visual Studio 2010 + Python Tools for Visual Studio 2010 beta, позже на CPython 2.7 + paver + concurrent.futures + lxml + Eclipse + PyDev. Сейчас присматриваюсь к среде разработки JetBrains PyCharm, хотя использование коммерческого продукта для подобных задач кажется расточительным.
По состоянию на сегодняшний день build/deplyment скрипты представляют из себя сборную солянку следующих библиотек:
- paver, BSD
- concurrent.futures, BSD
- lxml, BSD
- py-restkit, BSD
- simplejson, MIT
- http-parser, MIT
Так же я обращал внимание на следующие библиотеки:
- GitPython, BSD, работает, но не понадобилась
- PyActiveResource, MIT, не подошла из-за функциональных ограничений
- python-rest-client, GPLv3, не пробовал из-за лицензионных ограничений
Подписаться на:
Сообщения (Atom)