Блог Толика Вострякова

Живой опыт программирования, Python, Django, современные языки и немного фотографий
Блог - новые записи и ссылки (Atom)
 

Игры с Manager, Lock из multiprocessing модуля -

В одном проекте я использовал redis для хранения статистики, собираемой из нескольких процессов python на одной машине. Статистика складывалась в redis в hash или словарь. Попробовал заменить место хранения на словарь в питоне и вот, что из этого вышло:

from multiprocessing import Pool, Manager, Lock

lock = Lock()
manager = Manager()
stats_dict = manager.dict()

Проще не получилось, так как в разделяемой памяти (Shared Memory) нету такого класса, как словарь в multiprocessing модуле. Код, демонстрирующий использование:

def save_stats(x):
    lock.acquire()
    if not stats_dict.has_key('test'):
        stats_dict['test'] = 0
    for i in range(100):
        stats_dict['test'] += 1
    lock.release()

if __name__ == "__main__":
    pool = Pool(processes=4)
    pool.map(save_stats, [1, 2, 4, 5])

    print stats_dict

Как видим одновременно запускаются четыре процесса, выполняющие процедуру save_stats. После окончания, в поле 'test' окажется 400. Никаких race conditions. Вот думаю теперь насколько это лучше просто redis. Выглядит примерно одинаково по эффективности.

Комментарии: 2

TCP Server на питоне использующий все ядра процессора -

Предыдущие два дня я провел в реализации TCP сервера на питоне слушающего определенный порт и при выполнении запросов использующего все ядра процессора. Первая реализация была проста и использовала ThreadedTCPServer:

import SocketServer
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):     
    pass

class ThreadedTCPRequestHandler(SocketServer.StreamRequestHandler):     
    def handle(self):         
        data = self.rfile.readline()         
        result = ''         
        # request processing         
        ......         
        self.request.sendall(result)

server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler) 
server.serve_forever() 

И все бы хорошо, только python GIL в процессер обработки запроса не дает использовать больше одного ядра в один момент времени. К слову говоря, PyPy тут тоже не помогает. Можно конечно использовать вместо ThreadedTCPServer, ForkedTCPServer:

class ForkedTCPServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):     
    pass 

Действительно, все ядра начинают быть задействованы на 100%, но только массовое создание и уничтожение процессов ресурсоемкая штука, при большом кол-ве запросов в секунду. Хочется иметь заранее запущенный пул воркеров, которым можно было бы передавать запросы на выполнение. Если все воркеры заняты, то запросы становились бы в очередь. И в питоне есть такая штука!

 
from multiprocessing import Pool

if __name__ == "__main__":     
    WORKERS = 4     
    pool = Pool(processes=WORKERS, maxtasksperchild=500) 

, где maxtasksperchild кол-во выполненных задач одним воркером, после которого, он перезапускается, во избежание утечек памяти; WORKERS - кол-во процессов

Передать задачу на выполнение воркеру и получить результат можно так (внтури обработчика запроса):

 
def handle_task(data):    
    result = ''    
    # implementation    
    ...    
    return result

class ThreadedTCPRequestHandler(SocketServer.StreamRequestHandler):     
    def handle(self):         
        data = self.rfile.readline()         
        p = pool.apply_async(handle_task, args=(data, ))         
        result = p.get(timeout=30)         
        self.request.sendall(result) 

В результате мы имеем на каждый запрос отдельный поток и в каждом потоке обработка запроса происходит в свом заранее запущенном процессе. Результат возвращается после завершения выполнения задачи в процессе и передается TCP/IP клиенту.

На этом можно было бы остановиться, но в моем случае, походу обработки задачи, мне нужно было возвращать TCP/IP клиенту прогресс выполнения задачи и только в конце возращать результат. Первое, что пришло в голову - это использовать multiprocessing.Pipe, потом multiprocessing.Queue и в конец multiprocessing.Manager.Queue для передачи промежуточных результатов из процесса в обработчик TCP/IP запроса от клиента. К своему удивлению, не смотря на подробную документацию (http://docs.python.org/2/library/multiprocessing.html) меня ждал полный провал. В случае Pipe and Queue просто не работает на Python 2.7, в случае multiprocessing.Manager.Queue, как-то работает на Mac OS X, но очень глючно работает под Ubuntu. В общем использовать такие вещи из потока обработки запроса практически невозможно, работает это только в случае глобальных переменных, созданных внутри блока "if __name__ == "__main__":"

Но один выход нашелся, оказывается можно передать сам TCP/IP сокет из потока обработки в процесс воркера и сделать это можно вот так:

 
from multiprocessing import Pool 
from multiprocessing.reduction import reduce_handle, rebuild_handle 
import socket 
import SocketServer

def handle_task(data, h):     
    fd = rebuild_handle(h)     
    client_socket = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)     
    result = ''     
    # implementation     
    ...     
    client_socket.sendall(result)

class ThreadedTCPRequestHandler(SocketServer.StreamRequestHandler):     
    def handle(self):         
        data = self.rfile.readline()         
        h = reduce_handle(self.request.fileno())         
        p = pool.apply_async(handle_internal, args=(data, h))         
        p.get(timeout=30)


if __name__ == "__main__":     
    pool = Pool(processes=WORKERS, maxtasksperchild=500)     
    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)     
    server.serve_forever() 

Решение рабочее, но по ощущениям избыточное. Может у кого есть более простое решение этой проблемы?

Комментарии: 4

Intoduction in AI -

При ответе на вопросы в рамках office hours (то есть ответы на вопросы от учеников от преподавателей курса) было как минимум два интерсных вопроса-ответа. Далее вольный перевод по памяти с моими возможными добавками:

Ученик: Почему вы делаете эти курсы бесплатно для всех?

Себастиан: Мы делаем это из-за вас (показывает в экран). because of you! Так восхитительно делиться знаниями с таким огномных количеством людей. У меня никогда не было такой огромной группы!

Ученик: Что вы посоветуете читать?

Себастиян: Ничего не читайте. Выберите задачу, которая вам интересна (она может быть даже уже решена кем-то) и попробуйте решить ее. И когда вы ее решите своим способом, то потом вы можете почитать как она решалась другими. И я так же гарантирую (что-то вроде этого слова), что пока вы решаете такого рода задачи вы увидите около 10 задач, которые еще не решены и вот их-то и будет понастоящему интересно решать.

http://www.youtube.com/watch?v=Y6LF-_-pMgI

Комментарии: 0

Conceptor.ru: наш проект управления идеями -

 

Концептор: управление идеями

На этот раз пост не технический, а самый ни на что пиарный :) Полгода работы подошли к первому логическому рубежу, чем я и хочу поделиться и попросить потестировать наш сервис управления идеями. Статья на Хабре с описанием проекта прилагается.

 

Комментарии: 0

PyPy 1.5 -

Поигрался вчера с PyPy 1.5 на 64-битном Маке. Мой простой тест производительности отрисовки простого темплейта джанго показывает увеличение скорости всего процентов на 20. Честно говоря намного меньше чем официальный бенчмарк от команды разработчиков. Могу предположить, что значительный прирост наблюдается наверно только в Linux.

Из хорошего: можно создать виртуальную среду с помощью virtualenv, работают все библиотеки написанные на чистом Питоне. Даже установилась библиотека pylibmc для работы с memcached. Но не удалось поставить psycopg2 (но есть специальная версия pypy от Alex Gaynor, то есть одного из core developer). На удивление не поставился PIL, хотя в интернете пишут, что под версию 1.4 ставился.

Дайте знать, кто-то пробовал PyPy 1.5 под линукс, какой прирост скорости наблюдается? Удалось ли кому запустить PIL и psycopg2?

 

Комментарии: 1

тестирование django-simple-captcha -

Так как в блог уже достаточно давно сыплется достаточно большое количество спама и защита Akismet не помогает, то решил сегодня добавить в комментарии простую captcha от библиотеки django-simple-captcha, с упрощенным алгоритмом искажения символов. Посмотрю на ее эффективность в защите от спама и напишу о результатах где-то через месяц. Так же надеюсь, что она не доставит большого неудобства, тем кто действительно хочет написать мне ценный комментарий :)

Комментарии: 8

django_ses и Amazon SES -

  Не так давно Амазон запустил сервис Simple Email Service или сокращенно SES. Судя по отзывам в интернет, цены на отправку писем самые низкие, всего 10 центов за 1000 писем + трафик 15 центов за гигабайт исходящего трафика (первый гигабайт бесплатно). У конкурентов обычно в 10 раз дороже.

  И когда у нас в проекте появилась потребность отправки большого кол-ва писем, то решил попробовать SES. Сначала, идя по инструкции по установке и настройке, долго казалось, что отправлять письма можно только через спец. скрипт написанный на Perl. О боже, нужен был не только Perl, а так же к нему еще поставить ряд библиотек. После непродолжительной депресии, помогло наконец почитать README, находящееся в папке со скриптом. Там были даны простые инструкции по установке дополнительных библиотек! :)

  Параллельно, как написано в амазоновской инструкции, сделал запрос на открытие мне доступа к SES в production mode. Это значит, что я могу отправлять письма на любые адреса, а не только на проверенные. Подтверждение от амазона пришло в тот же день вечером. Оперативно.

  Но проблема отправки писем через скрипт из командной строки по прежнему не радовала. Как это интегрировать с джангой? Попытки настроить интеграцию с sendmail на сервере по описанию в доке Амазона не увенчалась успехом. И тут счастье наконец наступило в лице свежеиспеченной библиотеки django_ses. Оказалось умельцы уже сделали интеграцию с джангой за счет написания своего почтового бэкэнда. Плюс доступ к статистике отправки почты. Все просто и удобно.

  Будем пробовать на практике и понемногу допиливать django_ses :)

Комментарии: 0

Вышел новый celery 2.2: используем c redis -

Соответственно обновилась и библиотека django-celery, упрощающая работу с celery. В новой версии произошло существенное изменение: произошел переход с библиотеки carrot на kombu. Как пишут в релизе, kombu дает следующее:

  • встроенная поддержка Redis, Django ORM, SQLAlchemy, MongoDB, CouchDB и т.д.
  • целостная обработка ошибок с интроспекцией
  • возможность быть уверенным, что операция производиться посредством изящного обработчика соединений и канала ошибок (gracefully handling connection and channel errors)
  • сжатие сообщений (zlib, bzip2 или своя схема компрессии)

Полный список изменений

Переход на kombu в том числе значит, что библиотека ghettoq больше не нужна для работы с Redis. Все уже есть в самом celery.

Что такое Celery?

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

Установка django-celery и настройка для работы с Redis.

Очень удобно работать с celery через библиотеку django-celery. Ставим через pip + библиотеку работы с redis:

pip install django-celery
pip install redis

Заодно поставиться и celery и все нужные библиотеки. Останется только поставить Redis. Настройка проста, в settings.py нужно сделать всего две вещи: в INSTALLED_APPS добавляем "djcelery" и для работы через Redis добавляем следующий код:

import djcelery
djcelery.setup_loader()

BROKER_BACKEND = "redis"
BROKER_HOST = "localhost"
BROKER_PORT = 6379
BROKER_VHOST = "0"

Использование на примере

Для асинхронных задач: В папке приложения в проекте джанго создайте файл tasks.py. Задачи в таком файле будут автоматоматически распознаны celery при запуске. Пример задачи в файле tasks.py:

from celery.decorators import task

@task()
def send_email(from, to, subject, body):
  # send email...

Запускаем celery из папки проекта НЕ в демон режиме:

python manage.py celeryd -l info

Запуск в режиме демона

В коде, такую задачу вызываем простой командой:

send_email.delay(from, to, subject, body)

Читать дальше документацию

Комментарии: 3

Итоги блога за 2010 год -

В этом году я начал вести свой блог для разработчиков. Первый новый пост был добавлен в блог 16 февраля. За время прошедшее с этого момента, получились следующие цифры:

  • Блог посетило 6.000 уникальных человек и все вместе они просмотрели более 16.000 страниц
  • за год я написал 28 постов + 1 
  • через Google Reader блог читает 187 человк.
  • самой популярным оказался пост про south 0.7 + django 1.2

Спасибо что читали и оставляли свои ценные замечания! С новым годом, друзья!

Комментарии: 0

Django-sphinx. Продолжение про Sphinx 1.1 + Django + Postrgresql -

Fork of django-sphinx

В процессе работы над проектом (см. мой предыдущий пост для введения в тему) с использованием приложения django-sphinx, последний пришлось доработать до наших потребностей. Представляю мой fork от django-sphinx, в котором появились следующие возможности:

  • возможность фильтровать или исключать из фильтрации по первичному ключу (pk)
  • в метод set_options добавлен параметр only_sphinx, которая возвращает результат запроса из Sphinx не делая запроса в БД
  • добавлен метод only - обертка над одноименным методом django queryset
  • добавлен метод group_distinct позволяющий подсчитывать кол-во уникальных значений по любому аттрибуту внутри группы
  • в метод set_options добавлен параметр limit, позволяющий ограничить кол-во возвращаемых строк
  • подправлена генерация конфига Sphinx при использовании свойства DATABASES в settings.py, начиная с версии Django 1.2 и больше
  • добавлено свойство count к каждой строке результатов поиска - хранит кол-во записей в группе при использовании группировки

Интересные дополнительные возможности Sphinx

  1. Можно группировать по MVA атрибутам (см. предыдущий пост), то есть такой код вполне работает: Tag.search.group_by('types', SPH_GROUPBY_ATTR)
  2. Можно подсчитывать кол-во уникальных значений по какому-то атрибуту внутри группы: Tag.search.group_by('types', SPH_GROUPBY_ATTR).group_distinct('status')
  3. Интересные возможости по формированию строки поиска в расширенном режиме. Можно писать так: Tag.search.query('(белая кошка) | собака | ёжик').\
    set_options(mode='SPH_MATCH_EXTENDED2', rankmode="SPH_RANK_BM25", sort='SPH_SORT_RELEVANCE')
  4. По умолчанию Sphinx ищет по словам целиком с учетом морфологии указанного языка (русский, английский и т.д.). Но можно указать искать по вхождениям строк так: Tag.search.query('соба*'). Чтобы это заработало в sphinx.conf в разделе index application_tag нужно прописать следующие строки (начинать поиск начиная с 3 символов):
    enable_star     = 1
    min_prefix_len  = 0
    min_infix_len   = 3
Комментарии: 0

Ищу джанго-разработчика в проект iStrahovka -

Итак, на этот раз ищу профессионального Джанго-разработчика в проект которым я занимался около полугода. Сильные знания именно Джанго не обязательны, но профессиональным программистом нужно быть :) Программист ищется в проект  istrahovka.ru. Краткая суть проекта: веб-сервис подбора и покупки страховых продуктов от ведущих российских страховых компаний как для обычных пользователей, так и для страховых агентов. На данный момент доступно только два страховых продукта: ОСАГО и КАСКО и 5 страховых компаний. В ближайшей перспективе планируется взять второго разработчика для совместной работы над проектом.

Зарплата в районе 90 тыс. рублей. Возможно какое-то время поработаем вместе в качестве меня руководителя разработки :)

Передавайте эту вакансию своим друзьям и друзьям друзей и пишите в комментариях о кандидатах :)

Upd: Город Москва, но возможна удаленная работа. В процессе работы скорее всего потребуется пару раз (не чаще раза в месяц), приезжать в Москву. В общем это по ситуации, но скорее всего потребуется, поэтому лучше жить не слишком далеко от Москвы, чтобы вы могли приехать.

Upd: Вакансия закрыта.

Комментарии: 13

Sphinx 1.1 + Django + Postgresql -

В проекте, в котором я сейчас участвую, одним из центральных элементов является полнотекстовый поиск по базе данных продуктов объемом около 1 млн. записей. У ребят уже был реализован поиск, используя встроенные возможности Postgresql (поле типа TSVector и специальный полнотекстовый индекс). Но скорость поиска достаточно низка, плюс усложняющая логику реализация в ORM Джанги. Поэтому возникла идея попробовать поиск через Sphinx. С этого момента sphinx приятно удивил меня несколько раз своими возможностями, а удовольствие от работы с ним с каждым разом только возрастало :) Если в кратце, то оказалось, что sphinx заменяет собой не только полнотекстовый поиск в базе данных, но фактически вообще поиск в базе данных, а так же группировку и сортировку результатов. Итак, по порядку.

Установка

Во-первых, скачиваем архив с исходниками Sphinx-1.10beta. Там есть несколько вкусных плюшек, без которых мне не захотелось жить, по сравнению с версией 0.9.9 :) Компилируем и устанавливаем, с поддержкой Postgresql:

tar -xzf sphinx-1.10-beta.gz
cd sphinx-1.10-beta
./configure --prefix=/usr/local/sphinx 
--with-pgsql-includes=/opt/local/include/postgresql84 
--with-pgsql-libs=/opt/local/lib/postgresql84 --with-pgsql
make
sudo make install

Настраиваем линки к индексирующей утилите и демону Sphinx для удобства:

ln -s /usr/local/sphinx/bin/searchd searchd
ln -s /usr/local/sphinx/bin/indexer indexer

Django-sphinx

Для интеграции Sphinx с Django я попробовал библиотеку Django-sphinx и остался доволен. Как мне говорили, какое-то время назад она была еще сыровата, но сейчас она меня вполне устраивает и легко поддается расширению. Единственный недостаток пожалуй, что похоже она не будет в ближайшее время поддерживаться создателем David Cramer, так как похоже у него просто теперь нет на это времени из-за его перехода в команду проекта Disques. Но уже сейчас на GitHub есть 12 форков библиотеки с разными расширениями функциональности. Я в том числе собираюсь в ближайшее время создать свой :)

Установка: pip install django-sphinx

Настройка Sphinx и Джанго

Если хотите разобраться быстро, то просто пойдите на страницу django-sphinx по ссылке выше. Там достаточно понятно описаны основные возможности. Я же опишу все подробно и плюс несколько продвинутых фишек, для которых пришлось покопаться в исходниках. Итак, прежде всего нужно добавить в settings.py переменную:

# Sphinx 0.9.9 +
SPHINX_API_VERSION = 0x116

Для добавления поиска через sphinx с указанием веса каждого полнотекстового поля в модель Джанго пишем:

from djangosphinx.models import SphinxSearch

class Tag(models.Model):
    title = models.CharField(unique=True)
    description = models.TextField()
    types = models.ManyToManyFields(TagType, verbose_name=u'типы тэга')
    status = models.SmallIntegerField(choices=[(1, u'первый статус'), 
                   (2, u'второй статус')]) 

    search = SphinxSearch(weights={
            'title': 100,
            'description': 30
        })

Дополнительно при инициализации класса SphinxSearch можно задать название индекса, режим ранжирования результатов и т.д.

Далее нужно создать конфигурационный файл для sphinx. Тут нам тоже поможет django-sphinx, так как он умеет создавать либо полностью, либо примерно половину этого файла. Для этого добавим библиотеку djangosphinx в INSTALLED_APPS проекта. Затем, в корне проекта пишем:

./manage.py generate_sphinx_config <application name>

Для примера модели выше получаем:

source application_tag
{
    type                = pgsql
    sql_host            = host
    sql_user            = user
    sql_pass            = password
    sql_db               = database
    sql_port            = 

    sql_query_pre       =
    sql_query_post      =
    sql_query           = \
        SELECT id, title, description, status\
        FROM application_tag
    sql_query_info      = SELECT * FROM `application_tag` WHERE `id` = $id

   sql_attr_uint       = status
}

index application_tag
{
    source          = application_tag
    path            = /var/data/application_tag
    docinfo         = extern
    morphology      = none
    stopwords       =
    min_word_len    = 2
    charset_type    = utf-8
    min_prefix_len  = 0
    min_infix_len   = 0
}

Значения переменных sql_host, sql_user, sql_pass, sql_db, sql_port будет заполнено из вашего settings.py. application - название приложения в котором объявлена модель. Рекомендую сразу заменить morphology = none на morphology = stem_enru для учета морфологии русского и английского языков. Теперь вручную в будущий конфиг-файл нужно будет только добавить служебный раздел sphinx. У меня он такой:

searchd
{
    # какой порт и какой протокол "слушает" служба
    listen            = 3312

    # файл с логами
    log            = /usr/local/sphinx/var/log/searchd.log

    # файл с логами поисковых запросов
    query_log        = /usr/local/sphinx/var/log/query.log

    # PID file, searchd process ID file name
    # mandatory
    pid_file        = /usr/local/sphinx/var/log/searchd.pid
}

Все вместе сохраняем в файле sphinx.conf. В общем случае можете называть файл как хотите, но это название файла по умолчанию, который ищет в текущей папке демон searchd. Остается создать индексы sphinx командой: indexer --config sphinx.conf --all и запустить демон командой searchd.

Использование поиска через Sphinx в Джанго

Теперь самое интересное :) - что мы можем делать. Очевидно можем делать полнотекстовый поиск с учетом морфологии русского и английского языка:

Tag.search.query(u'тэгу')

Еще интереснее - можем искать так же по атрибутам. В нашем примере выше, атрибутом является поле status:

Tag.search.query(u'тэгу').filter(status=[1, 2])

Мы можем искать по диапазону:

Tag.search.query(u'тэгу').filter(status__range=[1, 3])

В версии 0.9.9 поддерживаются так же следующие типы атрибутов: sql_attr_bool, sql_attr_float, sql_attr_timestamp. В версии 1.1 дополнительно добавлен часто необходимый строковый тип атрибута: sql_attr_string. Следует отметить, что текстовый атрибут и другие атрибуты сохраняются вместе с основным индексом и поиск по ним напоминает поиск по полям в обычной базе данных, но быстрее. Впрочем текстового атрибута оказалось создателям sphinx мало, поэтому так же было добавлено специальное текстовое поле sql_field_string, которое одновременно является и текстовым атрибутом и полнотекстовым полем. Примечание: на данный момент django-sphinx не поддерживает текстовые атрибуты.

А теперь переходим к действительно интересным вещам: мы можем искать по ManyToMany полям в модели используя Multi Value Attribute (MVA). Это нам дает устранить еще одно узкое место баз данных в плане быстродействия и масштабирования - join таблиц. Сначала пропишем MVA атрибут в конфиге sphinx в конце раздела source application_tag:

sql_attr_multi = uint types from query; SELECT tag_id, typetag_id \
FROM application_tag_types

теперь мы можем искать по нему, как и по другим атрибутам:

Tag.search.query(u'тэг').filter(types=[1, 2, 3])

Радуемся дальше :) В sphinx 1.1 было добавлена поддержка поля sql_joined_field. Для чего оно нам? Представим стандартную ситуацию, например, у нас есть модель товар. К товару можно прикрепить несколько тэгов. Потом мы хотим найти все товары, через полнотекстовый поиск sphinx, у которых есть данный тэг. Без данного поля нам скорее пришлось бы сделать два запроса к sphinx. Сначала найти все тэги. Потом все товары у которых есть один из найденных тэгом. Теперь же мы можем без всяких join'ов обойтись одним запросом, сразу находя товары по их тэгам. Объявляем поле в конфиг-файле, например так:

sql_joined_field    = tags_text from query; SELECT product_id, title \
FROM application_product_tags apt left join application_tag at on at.id = apt.tag_id \
order by product_id ASC

Примечание: данные, должны быть обязательно отсортированы по товарам.

Не забываем переиндексировать Sphinx. Теперь при обычном полнотекстовом поиске, так же будет производиться поиск и по этому полю.

Еще одна радость использования Sphinx: мы можем группировать результаты поиска по любому атрибуту или группе атрибутов:

from djangosphinx.apis.current import SPH_GROUPBY_ATTR

Tag.search.query(u'тэг').filter(types=[1, 2, 3]).group_by('status', SPH_GROUPBY_ATTR)

В результате sphinx вернет для каждой группы лучший результат, а так же в специальном дополнительном поле @count число элементов в группе. К полю @count можно будет достучаться в дальнейшем так: item._sphinx['attrs']['@count']

И последняя возможность Sphinx, о которой хочу упоминуть в этой статье - вы можете сортировать как по весам, так и по атрибутам:

Tag.search.query(u'тэг').filter(types=[1, 2, 3]).order_by('status')

Дополнительно django-sphinx поддерживает команды select_related и extra, которые передаются в ORM джанго. Не проблема будет добавить так же ORM команду filter, если после поиска в sphinx мы хотим дополнительно отфильтровать результат с помощью базы данных. Мне же, на данный момент, удалось обойтись исключительно поиском через sphinx, так что дополнительной фильтрации не потребовалось.

Выводы

Как оказалось sphinx, помимо полнотекстового поиска, закрыл собой сразу несколько проблемных мест с быстродействием и масштабируемостью поиска. Благодаря использованию MVA атрибутов и sql_joined_field полей, а так же возможности сортировки и группировки результата. И все это вместе с в несколько раз более высокой скоростью поиска, а так же высокой скоростью переиндексации данных. В предстоящем релизе 1.1 будет так же добавлена возможность обновления индекса в реальном режиме.

Тем временем мое знакомство со sphinx продолжается и я еще обязательно напишу на эту тему.

Продолжение... 

Комментарии: 8

Nginx-0.8.52 с uwsgi модулем -

Похоже достойная замена Apache для развертывания python-приложений на сервере дошла до момента, чтобы это попробовать в деле. uwsgi сам по себе активно развивается. Уже реализована поддержка нового протокола Web3, чтобы приложения python 3.x смогли работать под web-серверами. Как только появиться возможность, обязательно попробую запустить проект под nginx + uwsgi.

Ссылки: nginx, uwsgi

Комментарии: 8

Time zones или временные зоны и pytz -

Питоновская библиотека pytz это просто благодать, сошедшая на меня сегодня :) Конечно большинство знает про эту бибилотеку, просто я впервый раз ее сегодня использовал. Если мы знаем название локальной time zone, то преобразовать серверное время в локальное очень просто (переход на летнее время тоже учитывается):

from pytz import timezone
from datetime import datetime

tz = 'Europe/Moscow'
server_time = datetime.utcnow()
client_time = timezone(tz).fromutc(server_time)

Update: Как подсказывает bw в комментариях, есть еще одна интересная библиотека dateutil

Комментарии: 4

Загрузка файлов на сервер через AJAX -

По наивности попробовал загрузить файлы на сервер, используя JQuery-команду post:

$.post(url, $("#myForm").serialize());

HTML-код формы, скажем такой:

<form id="myForm">
  <input type="text" name="text"/>
  <input type="file" name="image"/>
</form>

Оказалось через AJAX так делать нельзя, если в форме есть файлы :) Как же тогда сделать? Одно из решений - плагин "forms" к jQuery. В коде страницы пишем следующее:

script type="text/javascript" src="/media/js/jquery.form.js"></script>
<script type="text/javascript">
$(document).ready(function() {
  // bind 'myForm' and provide a simple callback function 
  $('#myForm').ajaxForm();
 
  // submit form
  $("#myForm").ajaxSubmit(
            url: url,
        iframe: true,
        dataType: 'json',
        beforeSubmit: function(arr, $form, options) {
                // some code here
            },
            success: function(data) {
                // some code here
                        }
        });
})

, где параметр iframe: true рекомендую установить, если идет загрузка файлов. Параметр dataType: 'json' - задает тип возвращаемых данных.

Попробовал работу плагина в Firefox 3.6, Chrome 5 и Internet Explorer 8. Работает без проблем. И на последок описание API плагина.

Комментарии: 13

Русские имена файлов в zip-архиве и Python -

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

Итак задача стояла: загрузить на сервер zip-архив картинок товаров, пройтись на сервере по всем именам файлов в архиве, производя над каждым файлом определенные действия. Следуют сказать, что названия файлов были чрезвычайно важны, так как название файла - это код товара. По этому коду в базе находился нужный товар и к нему крепился распакованный файл из архива. Все естественно работало на ура, пока названия были англоязычные, но как только коды товаров, а соответственно и названия файлов стали попадаться с русскими буквами все поломалось. И что же обнаружилось?

  1. Кодировка названий файлов в zip-архиве созданным под виндой оказалась не Windows-1251 и даже не KOI8-R, а CP866 (кодировка времен DOS). Под маком имена файлов кодируются в UTF-8.
  2. Работа с русскими названиями файлов в Питоне зависит от ниже лежащей операционной системы. Немного света на этот вопрос пролил раздел Unicode filenames. Совет из этого раздела "When opening a file for reading or writing, you can usually just provide the Unicode string as the filename, and it will be automatically converted to the right encoding for you..." оказался не всегда верным, так как на нашем виртуальном сервере под FreeBSD оказалась была задана кодировка ASCII для имен файлов и изменить ее без обращения в тех. поддержку не представлялось возможным (узнать кодировку можно с помощью команды sys.getfilesystemencoding() ). А без ее изменения попытка открыть файл с названием в формате unicode все падало. Оказалось универсальным методом является преобразовать строку в обычную 8-битную в формате UTF-8 и уже полученную строку подсунуть в функцию открытия файла open(filename, 'w').

Итак, код распаковывающий по-файлово zip-архив с русскими названиями файлов в нем, работающий как на архивах созданных под Windows, так и под Mac (сам код работает успешно под Mac и Unix) будет выглядеть так:

    from zipfile import ZipFile
    import os

    # путь для распаковки файлов
    path = os.path.join(settings.PROJECT_DIR, 'media/upload/products')

    f = ZipFile(filename, 'r')
    for name in f.namelist():
        try:
            unicode_name = name.decode('UTF-8').encode('UTF-8')
        except:
            unicode_name = name.decode('cp866').encode('UTF-8')

        # some analyzing of unicode_name and execute some actions
        # ...

        file_name = os.path.join(path, unicode_name)
        f2 = open(file_name, 'w')
        #f.extract(name, path) # works only for python 2.6+
        f2.write(f.read(name))
        f2.close()

    f.close()

Update: В комментарии подсказывают chardet - универсальный детектор кодировки

Комментарии: 6

Передача параметров по ссылке в Питоне -

Мне нужно было передать в функцию параметр, в функции он изменяется и снаружи функции видно изменение. То есть фактически передать в функцию переменную по ссылке. Оказалось в Питоне с этим хитро. Есть изменяемые и неизменяемые переменные. Зависит от типа. Например, строки неизменяемый тип, а списки изменяемый. Соответственно строки передаются в функцию по значению, а список по ссылке. Пример параметра списка:

def test(a):
  a.append('new element')

a = ['first element']
test(a)

Результат в переменной "а": ['first element', 'new element']

Если есть другие варианты явно указать, что параметр функции передается по ссылке, прошу в комментарии к посту.

Update: Кому интересно - загляните в комментарии, там тема расскрыта полностью.

Комментарии: 10

Печать документов из браузера -

Недавно закончил эксперименты с печатью "серъезных" документов из различных браузеров. Цель: возможно ли сделать документ с высокой точностью только с помощью HTML+CSS. (я имею ввиду, что документ визуально выглядит после распечатки, так же как и бумажный оригинал, отклонение не более 2-3 мм). Эксперимент проводил над следующими браузерами: IE 8, Firefox 3.6, Chrome 5.

Получается следующая картина:

- в IE8 и Firefox 3.6 можно добиться распечатки документа близкого к оригиналу, если все размеры в документе указаны в миллиметрах. В обоих браузерах есть настройки отступов страницы, печатать или нет header and footer и фон элементов.

- Google Chrome печатает нормально, но оказалась засада. У него вообще нет настроек параметров страницы перед печатью. В результате Chrome всегда печатает служебную информацию в header и footer каждой страницы и отключить это невозможно. Так же невозможно включить печать фона элементов (оказалось, что это отдельно нужно включать в настройках браузера).

- Opera показывает документ отлично от остальных браузеров. И распечатать пока тоже неудалось из-за поломки принтера.

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

Update: Печать под Мак из этих же браузеров + Safari (кроме конечно IE 8), выглядит хуже: невозможно настроить отступы и служебную информацию в header и footer.

Комментарии: 3

Преобразование документов в html -

Оказалось, что одним из лучших инструментов для конвертирования вордовских, экселевских, pdf и прочих документов в html являются Google Docs. Получаемый html-файл (плюс отдельно картинки) соответствует оригиналу и компактен. Сравнивал с возможностями MS Office и OpenOffice.

Комментарии: 0

tlstitle - криптографическая библиотека на чистом Питоне -

Потребовалось в проекте зашифровать данные достаточно надежным алгоритмом, например AES. Для этих целей есть конечно хорошая библиотека M2Crypto или есть еще PyCrypto. Но на сервере, где крутиться проект, нельзя поставить библиотеку, которая имеет в составе cишный код, так как это простой виртуальный сервер.

Покопавшись в интернете, не сразу, но нашел то, что надо. Криптографиеская библиотека tlslite, реализованная на чистом питоне.В том числе в ней есть быстрая реализация AES алгоритма. Правда пришлось закомментировать пару строк в файле tlslite/utils/Python_AES.py, чтобы не ругалось на то, что длина кодируемого текста не кратна 16. На самом деле работает с любой длиной текста.

Например, так можно зашифровать и расшифровать текст в джанго-приложении:

import base64
from django.conf import settings
from tlslite.utils.Python_AES import Python_AES

aes = Python_AES(settings.SECRET_KEY[0:16], 2, settings.SECRET_KEY[16:32])
s = base64.encodestring(aes.encrypt('test'))
print s

print aes.decrypt(base64.decodestring(s))
Комментарии: 2

следующая страница

 

 

Автор

Толик Востряков
Ник: magic
Мой ЖЖ

Интересное

psycopg2 + ctypes -

Переписанный psycopg2 для работы с ctypes под PyPy 1.6+

Simulating select_related() on a GenericForeignKey -

Django patterns, part 4: forwards generic relations Очень пригодилось для проекта - выборка всем GenericForeignKeys одним запросом к базе. Рекомендую, кто сталкивался.

python-handler-socket library -

Библиотека на питоне для работы с HandlerSocket - специальным плагином для работы с MySQL в режиме NoSQL. Производительность сравнима или превосходит memcached. http://l-o-n-g.livejournal.com/153756.html - описание использования и тестирования HandlerSocket

Realtime Web: TornadoIO + Socket.io -

Интересная серверная библиотека поверх Торнадо + клиентская часть в виде библиотеки Secket.io поддерживающая следующие протоколы для организации непрерывного канала с сервером: Websocket, Flashsocket, XHR multipart, XHR polling, JSONp, HTMLFile (IE only)

Django 1.3 Cache improvements -

В Django 1.3 систему кэширования довели до ума. Из новых прикольных штук: 1. можно указывать несколько кэш-бэкэндов (так же как в случае баз данных): новая переменная CACHES; 2. появилось понятие версии кэша (есть функция для инкрементирования версии); 3. можно задать свою функцию для формирования префикса для ключа; 4. Добавлена поддержка библиотеки pylibmc (самая быстрая из всех для работы с memcached)

django-multihost -

Приложение упрощающее работу с несколькими сайтами в одном проекте, меняя SITE_ID на лету в зависимости от названия домена.

django-test-utils -

Набор утилит для тестирования Джанго проектов. Спасибо Роме Ворушину за наводку

PyPy 1.4 -

Сравнение скорости работы Джанго с cPython 2.6.2

Быстрее в 7 раз! Наконец-то 64 битная версия! Версия уже используется в production

django-extauth -

Extended authorization framework for Django (also 1.2), including field-level permissions and role-based permissions

Javascript templates -

Сохраню пару ссылок на интересные шаблонизаторы на javascript, как замена шаблонам на сервере. Второй с поддержкой джанго-шаблонов. И еще один очень интересный tempest: jquery + django template синтаксис

django packages site -

Django Packages - reusable apps, sites, tools, and more for your Django projects. Очень полезный сайт. Все пакеты разбиты по категориям. Можно сравнить пакеты с одинаковой функциональностью по популярности, продолжается ли еще его разработка и т.д. и выбрать лучший.

django-sentry -

Логирование в базу джанго - ошибок, замена стандартной отправке писем об ошибках. Удобная панель просмотра результатов логирования. При использовании нескольких баз данных (Django => 1.2) позволяет указать базу в которую будет производиться логирование. Альтернативное джанго-приложение для логирования: django-lumberjack

django-formwizard -

Django-formwizard is a reusable app to work with multi-page forms. Besides normal Forms, it supports FormSets and ModelForms. То есть, если нужно разбить форму на несколько страниц (шагов) и сохранить данные из всех шагов в базе на последнем шаге, то django-formwizard - это то что нужно. Данное приложение постепенно идет на замену стандартного form-wizard.

Django Admin Tools -

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

Релиз Django 1.2 -

Выдержка из текста релиза: "После многих месяцев работы, мы с гордостью заявляем о выпуске Django 1.2. Так много крутых штук в этом релизе, что даже простое перечисление не может дать полной картины. Вы должны просто пойти прочитать заметки о релизе, чтобы все это увидеть, а затем перейти на страницу скачивания и урвать себе копию."

Скорость разработки веб-приложений на Django -

В статье описывается измерение скорости разработки веб-приложений на Python + Django по сравнению с ASP.NET. В результате разработка на Python + Django оказалась в примерно в два раза бестрее, чем на ASP.NET

Using CPython extension modules with PyPy natively -

Парни не перестают радовать и скоро выпустят поддержку модулей расширения CPython. С моей точки зрения останется еще поддержка 64-битного режима и желательно Python 2.6-2.7 и PyPy действительно можно будет рассматирвать как замену CPython, дающую действительно большой прирост скорости

Erlang Factory SF Bay Area 2010 -

Так как в прошлом я программировал на Erlang, то тема развития этого языка мне близка. На странице представлены презентации многих интересных докладов с прошедшего Erlang Factory SF Bay Area 2010, такие как: Erjang - A JVM-based Erlang VM, Erlang SMP support - behind the scenes, Fast Enough (применение NIF, доклад от Майкрософт), JavaScript CouchApps with CouchDB, Latest news from the Erlang/OTP team at Ericsson и другие.

Вышел Python 2.6.5 -

19 марта был выпущен релиз Python 2.6.5, исправляющий несколько дюжин багов. Новый релиз уже доступен в мак-портах. Новой функциональности в ветке 2.6 больше не предполагается.

Thread-safe Django cache backend for pylibmc -

Реализация потокобезопасного кэш-бэкэнда для Джанго с использованием библиотеки pylibmc.

Hidden python features -

Отличная подборка интересных особенностей языка Питон в одном месте.

PyInstaller -

Программа для конвертирования приложений на Питоне в исполняемый файл под Windows, Linux и даже под мой любимый Mac OS X (пока из транка) со всеми нужными библиотеками для работы. Целью проекта является автоматическое отслеживание зависимостей со сторонними распространенными библиотеками прямо "из коробки" без каких либо дополнительных настроек. Проект активно развивается, в баг-трекере есть даже тикет для поддержки Джанго :)

Pip, virtualenv и virtualenvwrapper -

Рома Ворушин, понятным языком про pip, virtualenv и virtualenvwrapper. Про такие возможности pip я и не знал :)

johnny-cache -

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

Деревья в админке django -

Простой вариант подключения дерева в админку Джанги. В комментариях к статье есть ссылка на еще один вариант подключения дерева в админки, который на вскидку еще проще: FeinCMS tree editor И в завершении темы отличная библиотека для django работы с деревьями: treebeard

New GIL in Python 3.2 -

The new GIL does not eliminate the GIL--it makes it better. Namely, a significant reduction in all of that thrashing and extra signaling overhead

Описание новой функциональности Django 1.2 -

With each new release, Django offers new features and techniques to simplify web development. Django Advent brings you articles about these new features and the techniques they make possible, often written by the feature's author.

Context Manager for timing -

Прекрасный пример кода: одновременно Context Manager и декоратор для измерения времени выполнения кода

Материалы продвинутого уровня по Питону -

Подборка книг и статей для углубленного изучения языка

Easy fabric deployment -

Fabric- библиотека, позволяющая легко разворачивать вашу систему на боевых или stage серверах запуском всего лишь одной команды

django-pingback -

Джанго приложение от Ивана Сагалаева для реализации tracebacks, которое я скоро буду использовать в этом блоге

Декораторы Python -

Простое и понятное описание декараторов в Питоне от Ромы Ворушина с примерами кода