5 мар. 2015 г.

Интересный способ привлечь внимание пользователей bitbucket

Сегодня ночью мне пришло письмо от bitbucket, о том что не знакомый мне человек дает мне грант на запись в репозиторий. В названии репозитория доменное имя, по которому замещен интернет-магазин.
Грант, очевидно, был отозван сразу. Мне естественно, стало интересно посмотреть что там на сайте. Тему этого сайта для себя считаю закрытой, но какая-то более специализированная история, могла бы меня заинтересовать. Например, так можно раскручивать сервис непрерывной интеграции или аутсорс тестирования.
Для того что бы выдать грант пользователю достаточно знать его ФИ или username. Такие данные не сложно получить с помощью API bitbucket'а http://restbrowser.bitbucket.org/.

24 февр. 2015 г.

Timeout декоратор

Если функция будет работать долго, и при этом нужно чтобы она работала не дольше определенного количества секунд, можно применить такой вот декоратор:

import errno
import os
import signal
from functools import wraps


class TimeoutError(Exception):
    pass


def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
    """
    @timeout(60)
    def long_functions():
        <your code here>
    """
    def decorator(func):
        def _handle_timeout(signum, frame):
            raise TimeoutError(error_message)

        def wrapper(*args, **kwargs):
            signal.signal(signal.SIGALRM, _handle_timeout)
            signal.alarm(seconds)
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result

        return wraps(func)(wrapper)

    return decorator

Lock в файл или в redis?

Иногда нужно гарантировать эксклюзивное исполнение функции для определенного набора данных. Например, только одно одновременное изъятие данных из какого-либо источника. На помощь в таких ситуациях приходят локи.

Проще всего создать какой-нибудь lock-файл и проверять его существование. Если есть файл - lock включен.
import os
from contextlib import contextmanager


class LockedException(Exception):
    pass


@contextmanager
def file_lock(lock_file):
    """
    with file_lock('/tmp/file.lock'):
        some code here
    """
    if os.path.exists(lock_file):
        raise LockedException('LockedException')
    else:
        open(lock_file, 'w').write("1")
        try:
            yield
        finally:
            os.remove(lock_file)

Жалко напрягать HDD такими мелкими операциями. Попробуем делать lock в redis:
import redis
from contextlib import contextmanager


r = r = redis.StrictRedis(host='localhost', port=6379, db=0)


class LockedException(Exception):
    pass

@contextmanager
def redis_lock(lock_id):
    """
    with redis_lock('unique_redis_key'):
        some code here
    """
    if r.get(lock_id):
        raise LockedException('LockedException')
    else:
        r.set(lock_id, 1)
        try:
            yield
        finally:
            r.delete(lock_id)

Теперь проверим какой вариант быстрей:
if __name__ == '__main__':
    import time
    from numpy import mean


    count = 10000

    times1 = []
    for i in xrange(0, count):
        start = time.time()
        with file_lock('/tmp/file.lock'):
            pass

        times1.append(float(time.time() - start))

    times2 = []
    for i in xrange(0, count):
        start = time.time()
        with redis_lock('redis_lock'):
            pass

        times2.append(float(time.time() - start))

    print 'file lock:', max(times1), min(times1), mean(times1)
    print 'redis lock:', max(times2), min(times2), mean(times2)

Результаты:

maxminavg
file_lock0.08177495002750.000284910202026 0.000996773791313
redis_lock0.00140619277954 0.000319957733154 0.000346991157532

Lock в redis в 3 раза быстрей.

12 янв. 2015 г.

Shiningpanda и jenkins

Надкусил собаку, при настройке CI на базе jenkins. Проглотить мешают постоянные всякие мелочи. Например из последнего: плагин Virtualenv Builder [Shiningpanda], с помощью которого можно более лучше развертывать виртуальные окружения python, принимает настройку "name". По документации, настройка соответствует названию virtualenv окружения. Изменение этой опции не влияет на фактическое название окружения (по факту получается что-то вроде c934fdf).
При всем этом, Shiningpanda реально экономит время при создании CI для python проекта.

Сборка:
[Virtualenv Builder]
pip install pytest pytest-cov
$PYTHON_EXE setup.py install
py.test --junitxml junit.xml --cov-report xml --cov src tests.py

Постсборка:
[Cobertura coverage]
coverage.xml

[juint]
result.xml

И далее в постсборке запускает выкатка библиотеки и/или обновление девелоперского-стейджа.

А как в ваших компаниях реализован CI?

11 янв. 2015 г.

Отправка push-сообщений на мобильные устройства

Вчера меня в очередной раз спросили "как и чем лучше отправлять пуши?" Отвечаю: если у вас не очень много сообщений в месяц - идеально использовать http://urbanairship.com/. В нем есть много "богатой" аналитики прямо из коробки и он поможет вам акцентировать внимание на том, что действительно важно в этом вопрос: длинна сообщения, формат, скорость и гарантии доставки, отслеживания и аналитика. Есть бесплатный ознакомительный период, дающий максимум возможностей, но и бесплатной версии хватит, чтобы отправлять пуши на android, ios, win phone.
Если же количество отправляемых ежемесячно сообщений большое, а квартиру Москве продавать жалко - пишите свое решение, как поступили мы в Anews. В основе нашего решения django-push-notifications, который, в свою очередь, использовал код двух более низкоуровневых библиотек, которые постепенно были улучшены (без возврата кода в оные). Мы для себя улучшили поддержку юникода при отправке сообщений на iOS и добавили ttl в андройде (последние версии библиотеки ttl уже есть), обработку ответов о GCM сервера.
На данный момент на Anews мы почти ежедневно отправляем 500 тысяч сообщений, которые улетают к пользователям в среднем менее чем за 12 минут.

Блогинг на самописном джвике

В общем, эксперимент с блогом на самописном джвике можно считать завершенным. Больше всего этот опыт похож на попытку создать свою собственную почту вместо использования gmail - есть ощущение защищенности от Большого Брата, но нет тех печенек, которыми он заманивает на темную сторону.
Самая главная печенька, которой мне не хватало - отсев спам комментариев. Я натурально ловил себя на мысли, что не хочу заходить в админку блога, потому что там снова тонны скриптованных комментов с предложением сыграть в онлайн казино и сразу после этого медикаментозно увеличить себе пенис. Буэ.
После переноса постов из старого блога в новые прилетело сразу несколько комментов, которые я не получал к постам более года, потому как на том движке у меня не было старых подписчиков. Приятно получил комментарии к посту, но радость омрачает осознание того, что подписчики получили сразу пачку старых постов в RSS-ленте. Не удивительно, что общий тон комментариев раздраженно-осуждающий.

10 янв. 2015 г.

Интервью на openmonte.com

Очень интересный для меня опыт - интервью для сайта openmonte.com. Опубликовано почти месяц назад, но до этого было как-то не до блога. Паааадписывайтесь, ставьте лайк и др. и пр.! Что там еще нужно писать в таких случая? )