вторник, 15 апреля 2014 г.

SMTP сервер на python для разработки с сохранением писем.

Для личных нужд немного раcширил SMTP сервер для разработки, который можно запустить имея голый python.

python -m smtpd -n -c DebuggingServer localhost:1025

Все, что я добавил - это сохранение писем в указаный каталог, а то просматривать html письма в консоли совсем не удобно.

python dev_smtpd.py -s /path/to/save/mails/

По умолчанию используется DebuggingServer и порт 1025 на localhost.

Исходник лежит на GitHub Gist

понедельник, 7 апреля 2014 г.

Celery и долгие задачи.

Последнее время на проектах часто сталкиваюсь с celery-тасками, которые на момент написания обрабатывали небольшой объем данных, но со временем этот объем увеличивался и таска этого не ожидала. Например, у вас есть некая выборка пользователей которым нужно разослать письма, и со временем этот объем будет увеличиваться с приходом новых пользователей. Такие таски время от времени приходится переделывать на обработку больших объемов.

Предположим, у нас есть таск:

@task
def send_digest():
    users_to_send = User.objects.to_send_digest()
    for user in users_to_send:
        send_digest_email(user)

Один из вариантов - это увеличить время выполнения, но опять же это всего лишь временное решение. Мы будем разбивать наш массив на части и обрабатывать их, то есть мы будем брать первую часть, обрабатывать ее и ставить в очередь эту же таску со следующей порцией.

@task
def send_digest(offset=0, limit=0):
    users_to_send = User.objects.to_send_digest().order_by('id')[offset:offset + limit]
    for user in users_to_send:
        send_digest(user)

    if users_to_send.count() == limit:
        send_digest.delay(offset + limit, limit)

В конце мы проверяем количество объектов в массиве, если оно равно нашему лимиту, то скорее всего еще есть данные, если же количество меньше лимита, значит это была последняя порция.

пятница, 7 февраля 2014 г.

Как найти коммит "после чего поломалось"

Не многие знают, что у системы контроля версий git есть механизм поиска коммита "который сломал". Называется он - bisect.

Чтобы начать его использовать, нужно выполнить три команды: запустить сам bisect, указать известный нам хороший коммит(тот в котором ошибки еще небыло) и указать что текущий коммит плохой(содержит ошибку).

$ git bisect start
$ git bisect good XXXXXX
$ git bisect bad

Git каждый раз будет переключаться на "средний" из списка коммитов, которые находятся между "bad" коммитом и "good" коммитом. Проверив наличие ошибки, вы указываете, каким является текущий коммит ("хороший" или "плохой") и в зависимости от этого git переключается на коммит ниже текущего или выше. И таким образом, git, будет двигаться пока не найдет первый коммит "который сломал".

понедельник, 6 января 2014 г.

iOS: создание событий в календаре

Сейчас постараюсь обьяснить, как создавать события в календаре.

Можно использовать календарь по умолчанию для добавления событий или создать свой, мы будем создавать свой. Так как календарь можно получить только по его идентификатору, то его нужно запомнить после создания календаря. Для этого будем использовать хранилище настроек NSUserDefaults. Так же нужно не забывать что пользователь может в любой момент удалить созданый нами календарь, поэтому всегда нужно проверять получили ли мы дескриптор календаря или нужно создавать его заново.

Доступ к календарю нужно запрашивать у пользователя. Добавление событий будет всегда проходить через вызов метода проверки доступа, но сам запрос у пользователя будет только один раз.

пятница, 3 января 2014 г.

Передача обьектов между UI\w*View

Опять взялся за ковыряние iOS. Постараюсь внятно описать три способа передачи обьекта из одного UI\w*View(UIViewController, UITableViewContoller...) в другой.

  • перед отображением новой вьюхи без Storyboard
  • перед отображение с использованием Storyboard
  • передача обьекта на предыдущую вьюху

пятница, 26 апреля 2013 г.

Конфиг в erlang приложениях

Решил описать как правильно создать конфиг для erlang приложения, так как сам с этим немного помучался и не совсем однозначно это описано в официальной документации.

Предположим у нас есть приложение someclient, которому нужны настройки такие как логин и пароль. Для этого нам придется создать три файла: само приложение, файл описания приложения и файл конфигурации.

Приложение у нас будет просто выводить значения конфигурации "someclient.erl"

-module(someclient).
-export([start/0]).

start() ->
    application:start(?MODULE),
    io:format("all config values: ~p~n", [application:get_all_env(?MODULE)]),
    {ok, ConfigValue} = application:get_env(?MODULE, login),
    io:format("value form config: ~p~n", [ConfigValue]),
    {ok, AppValue} = application:get_env(?MODULE, password),
    io:format("value from app: ~p~n", [AppValue]).

Теперь создадим файл описания приложения "someclient.app". В этом файле мы тоже можем указать конфигурационные параметры. Но как мы увидим при выполнении, если в конфигурационном файле есть такие же ключи, то они имеют более высокий приоритет и заменят значение своим.

{application, someclient, [
    {env, [
        {login, "login from app"},
        {password, "password from app"}
    ]}
]}.

Ну и сам файл конфигурации “someclient.config”

[{someclient, [
   {login, "login from config"}
]}].

Запускать все это нужно предварительно скомпилировав

$ erl -compile someclient.erl; erl -noshell -s someclient
all config values: [{login,"login from app"},
                    {password,"password from app"},
                    {included_applications,[]}]
value form config: "login from app"
value from app: "password from app"

Как видите, значение логина перезатерлось значением из конфигурационного файла.

среда, 20 марта 2013 г.

Предотвращения повторного запуска скрипта

Достаточно распространенная задача сделать так, чтобы скрипт не запускался, если уже есть его работающая копия.

Часто для этого используют pid файл, но его нужно удалять по завершению выполнения. И если наш скрипт свалится по ошибке и pid файл не удалили, то и запускаться дальше он не будет.

Также можно вызывать команду ps и парсить ее ответ.

Но есть и более удобный вариант, который укладывается в пару строк и не боится ни падений по эксепшену, ни выключения питания. И это модуль fcntl с вызовом его flock.

пример кода:

#!/usr/bin/env python
import fcntl
import time

def main():
  time.sleep(5)

if __name__ == ‘__main__’:
    fl_lock = open(‘our_script.lock’, ‘w’)
    try:
        fcntl.flock(fl_lock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError:
        print ‘Another copy already run’
    else:
        main()