Четыре

У меня есть два доказательства существования этого мира.

Во-первых, это девочка Настя. Когда я вижу её, я осознаю своё существование, я вижу в ней себя другого, вспоминаю кто я есть.

Во-вторых, это играющие собаки. Я их видел одним морозным утром в центре города. Это были щенки, довольно большие, жизнь била в них ключом. Люди стояли на остановке, зябли, кутались в свои шарфы и капюшоны, а собаки играли на снегу и это было какое-то иное проявление жизни, как будто ножом резанули занавесь и оттуда бьёт свет, рассекая клубы пыли, которыми ты живёшь и дышишь. Я рассказал об этом одному человеку и она сказала, что тоже их видела — этих щенков. Тогда я и понял, что они были настоящие.

Три

Я думаю, всем известна проблема немытых тарелок. Она формулируется примерно так: “количество немытых тарелок стремится к совокупному количеству мытых и немытых тарелок”. Конечно, можно попробовать завести жену, но есть ещё один трюк. В отличие от первого никаких побочных эффектов :) Просто оставляем на кухне одну глубокую и одну плоскую — остальные убираем в чулан. То же самое делаем со столовыми приборами — одна вилка, одна столовая ложка, одна чайная ложка. Всё! Теперь чтобы поесть приходится мыть грязную тарелку. ЧТД.

Два

Я решил таки не удалять блог — пускай будет :) На новый год почти не пил, только в саму новогоднюю ночь. Потом не мог включить пару дней интернет — было время помедетировать в оффлайне над старыми наработками.

Сделал новое приложение django-urlauth. Оно позволяет добавлять к любой ссылке специальный код, который автоматически авторизует пользователя, когда он зайдет на сайте. Большинство людей знает такие ссылки по письмам из социальных сетей. Более-менее подробная документация доступна здесь: http://bitbucket.org/lorien/django-urlauth/src/tip/docs/index.rst

Решил постепенно рефакторить django-account. Сначала хотел забросить его совсем, а потом думаю — пускай живёт. Кстати, оно теперь юзает django-urlauth :)

Ну и из pybb повырезал всякий хлам. Например, вырезал декоратор render_to, он есть в django-account, который стал новой зависимостью pybb. Зато теперь pybb не зависит от PIL и sorl-thumbnail т.к. я вырезал поле для аватарки. Заводите аватарку в главном профиле вашего сайта, если она вам нужна :)

Всё, о чём я написал, можно пощупать на битбакете.

Один

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

Я вот думаю, может быть, просто писать в блог всякую поебень, которая в голову идёт. Во всяком случае python-поебень в голову уже давно не идёт. Скуууучно.

Год кончается. И хотя деление на года всего лишь условность, но она как и все условности реально влияет на наше поведение, так что можно говорить, что условность вполне материальная. Я чувствую эту 2009-ю условность, как очень приятный отрезок времени.

Хотя… писать мысли публично, есть в этом ловушка, я буду дрочить на фидбеки, мне будет приятно от комментариев, не знаю, куда деться от этого онанизма.

Я бы может даже удалил этот блог, но мне с него 50 рублей капает в день с продажи ссылок и моя мелочная душёнка жидится сотворить акт разрушения в угоду ежедневной сотне полтинников.

Как мерзко, 50 рублей в день. Не важно :) Будем улыбаться и есть конфеты. С новым годом, с новой болью, с новым счастьем :)

Статус проекта pybb

Зимой 08-09 я активно работал над проектом pybb, затем весной я забил на проект и вот последний месяц опять работаю :) В течении года от нескольких человек поступили существенные контрибуции, за что им большое спасибо. Правда некоторые я вырезал уже, потому что понял, что проект начинает превращаться в очередной all-in-one кухонный комбайн. Из существенного — были оторваны личные сообщения, были вырезаны левые поля типа location, site, icq и т.д. из форумного профиля, была вырезана генерация специфических вещей из главных вьюшек типа index, category, topic и перенесена в template tags, было вырезано велосипедное поле для аватарки с ресайзом и заменено на поле из пакета sorl-thumbnail, всего и не припомню. Суть этих изменений в том, что pybb должен содержать лишь базовый функционал форумного движка, который каждый уже сам сможет расширять при желании для нужд конкретного проекта.

Я подумываю также вырезать шаблоны из стандартной поставки pybb и выложить их в виде отдельного пакета. Кстати, один уже такой пакет я создал — это pybb-templates-fluxbb. Шаблоны, имитирующие вид форума fluxbb (punbb в девичестве). Большая часть шаблонов уже сделана, остались правки по мелочи и i18n. Посмотреть на то как выглядят шаблоны можно тут http://pydev.ru

Отсутствие шаблонов в базовой поставке позволит более вдумчиво разносить JS-логику и template тэги. Часть будет специфичной для шаблона, а часть будет общая для всех шаблонов т.е. будет лежать в пакете pybb. Пример общей js логики — скрипт для прорисовывания дополнительных файловых полей в форме для создания сообщения. Ещё один пример: скрипты markitup редактора.

Я не думаю останавливаться на достигнутым и надеюсь вырезать что-нибудь ещё. Возможно, генерацию email извещений — был ведь какой-то стандартный пакет, возможно, им заменю :-)

Также, я думаю, имеет смысл создать проект с именем типа pybb-standalone, который будет представлять из себя готовый django-проекты, который можно будет поднять на сервере с минимальными усилиями. От текущего варианта установки волосы дыбом встают.

Приятно, что на базе pybb работают несколько живых форумов:

Установка satchmo 0.9 в virtualenv

Читаем quickstart и делаем:

lorien@lorien:~$ cd /web
lorien@lorien:/web$ mkdir supershop
lorien@lorien:/web$ cd supershop
lorien@lorien:/web/supershop$ virtualenv .env
New python executable in .env/bin/python
Installing setuptools............done.
lorien@lorien:/web/supershop$ source .env/bin/activate
(.env)lorien@lorien:/web/supershop$ easy_install pip
...
(.env)lorien@lorien:/web/supershop$ pip install -r http://bitbucket.org/chris1610/satchmo/raw/tip/scripts/requirements.txt
...
(.env)lorien@lorien:/web/supershop$ pip install -e hg+http://bitbucket.org/chris1610/satchmo/@v0.9#egg=satchmo
...
(.env)lorien@lorien:/web/supershop$ clonesatchmo.py
...
ImportError: No module named satchmo_skeleton
(.env)lorien@lorien:/web/supershop$ ln -s /web/supershop/.env/src/satchmo/satchmo/projects/skeleton /web/supershop/.env/lib/python2.5/site-packages/satchmo_skeleton
(.env)lorien@lorien:/web/supershop$ clonesatchmo.py
...
Store installation complete.
You may run the server by typying: 
 cd store 
 python manage.py runserver

Библиотека для sape - рефакторинг

Так получилось, что я в очередной раз переписал библиотеку для работы с биржей ссылок sape.ru. В течение нескольких месяцев я пользовался библиотекой linkexchange, про которую уже писал раньше. Плюсы linkexchange — автор оперативно фиксит глюки. Минусы — глюки есть, за время использования я наткнулся на три штуки, о которых прямо или косвенно были посланы багрепорты. Свои глюки роднее — я решил вернуться к использованию самописаного модуля, первую версию которого я разработал ещё год или два назад. Как всегда переписал всё с нуля. Гавное отличие новой версии библиотеки от прежних — я отказался от обновления локальной базы ссылок прямо в коде сайта при обработке чьего-либо запроса. Ноги этого подхода растут из PHP-библиотеки, в которой такой подход оправдан т.к. позволяет использовать библиотеку большому количеству веб-мастеров, даже тем, кто не знает что такое cron и с чем его едят. В моём же случае мне наплевать на людей, которые не умеют использовать cron, поэтому я разделил библиотеку на две части: client и provider. Provider умеет обращаться к серверу sape.ru, забирать оттуда дамп базы данных, парсить его и сохранять локально в формате эффективном для быстрой выборки данных. А client это оочень маленький код, который просто умеет по данному ключу делать выборку данных, по сути, можно обойтись и вовсе без клиента т.к. всё равно всё сводится к вызову функции sape.provider.Provider::read_database_key. Таким образом теперь глюки sape.ru сервера не влияют на работу сайта т.к. базу обновляется скриптом, вызываемым по крону. Другое новшество библиотеки — я храню локально ссылки в DBM-базе, с которой работаю через модуль anydbm. Все говорят, что этот dbm — быстрая штука, если честно, я ещё не проверял :D Но должно быстрее быть старого подхода, когда я каждый раз считывал в память сохранённый дамп, десерилизовывал его с помощью библиотеки phpserialize и только затем уже искал нужный ключ.

Подробности о том, как использовать библиотеку в Django и неDjango проектах, читайте в README

Сорцы как всегда на битбакете. А ещё я научился оформлять distutils-пакеты и регистрировать их на PyPI. Так что либу можно ставить также через easy_install и pip. Обо всех глюках просьба писать на мыло.

Извлечение favicon сайта

Когда есть список ссылок, то для визуального украшательства можно использовать фавиконы, рисуя их слева от ссылок. Так, например, делает яндекс в результатах поиска. Я тоже решил побаловаться с иконками на странице http://pydev.ru/links/ Для чего была срочно написана библиотека favicon. Забрать можно с битбакета по адресу http://bitbucket.org/lorien/favicon

Использовать очень просто:

from favicon import get_favicon_from_url
print get_favicon_from_url('http://google.com')

Что делает функция get_favicon_from_url? Она скачивает страницу по указанном урлу и рыщет в егоном HTML коде. Если удаётся найти нужный link тэг, то возвращается его href аттрибут, если нет, функция не сдаётся и пытается закачать картинку со стандартного адреса http://<hostname>/favicon.ico, если файл найден (200 http code) и его mimetype начинается с “image/”, то функция возвращает стандартный адрес картинки. Если ничего найти не удалось, возвращается None

Запуск django сайта через nginx + superfcgi

Я вчера перевёл все свои сайты на связку nginx + fastcgi-демон. Раньше они работали на связке nginx + apache/mod_wsgi. Даже не знаю, зачем я это сделал. Да и какая разница :o) Я сейчас просто опишу как соединять nginx с fastcgi-django-демонами через библиотеку superfcgi и надеюсь, кому-нибудь это будет полезно.

Так вот, для начала нада поставить superfcgi библиотеку. Кстати, её автор barbuza. Ставится банально: pip install superfcgi. Только не забудьте зависимости поставить предварительно. В debian надо ставить такие пакеты: gcc, python2.5-dev (если у вас 2.5 python), libfcgi-dev. Ещё надо multiprocessing либу поставить в случае python2.5, правда, Виктор говорит, что она уже автоматически ставиться. Но, если что, имейте в виду.

После установки superfcgi надо прописать ‘superfcgi’ в settings.INSTALLED_APPS. Это сделает доступной команду manage.py runserupfcgi, с помощью которой можно запустить процесс, который позволит общаться с nginx через fastcgi-интерфейс.

Проблема в том, что надо как-то контролировать этот процесс: уметь его останавливать, например. Команда manage.py runsuperfcgi этого не умеет, поэтому я использую дополнительный bash скрипт. Пощупать его можно тут: http://bitbucket.org/lorien/spawn/src/tip/spawn.sh

У меня для каждого сайта есть свой маленький spawn.sh, в котором я задаю одну переменную $SITE и затем делаю include главного spawn.sh. Например, для запуска веб-мозгов у меня такой скрипт:

#!/bin/bash
SITE="web_brains"
source /home/web/bin/spawn.sh

На всякий случай, если вдруг сайт упадёт или будет сделан rebot сервера у меня в кроне каждую минуту стартуются все сайты. Тупо, да, зато работает :D Конечно, если сайт уже запущен, то копия не запускается, в spawn.sh учтёт этот момент. Каждую минуту я пытаюсь запустить все сайты таким скриптом:

#!/bin/sh

dirs=$(find /home/web/web -maxdepth 1 -type d)
for dir in $dirs; do
    echo "Checking directory $dir"
    cd $dir;
    if [ -e spawn.sh ]; then
        echo "Found spawn.sh"
        ./spawn.sh $1
    fi
done

Кроме django-сайтов я запустил через superfcgi также один trac-проект. Для этого я сделал следующее, создал в директории проекта файл trac_wsgi.py

import os

os.environ['TRAC_ENV'] = '/web/pybb.org'
os.environ['PYTHON_EGG_CACHE'] = '/web/cache/trac/pybb.org'

import trac.web.main
app = trac.web.main.dispatch_request

Это просто создание WSGI приложения, обслуживающего trac-проект. Теперь его можно подцепить к superfcgi библиотеке. Библиотека superfcgi предоставляет команду superfcgi, которая в параметре —app принимает название модуля, где находится wsgi-приложение. Описываю я всё довольно сумбурно, но писать подробнее не могу — шибко лень :) Я просто это всё напишу здесь, потом кто-то будет искать через google, наткнётся, прочитает, что-то поймёт. Я сам иногда на свои старые посты натыкаюсь и иногда даже нахожу то, что успел забыть :D

В общем, spawn.sh файл для запуска trac такой:

#!/bin/sh
SITE="pybb"
SOCKET_FILE="/var/run/www/pybb.sock"
NOHUP="1"
COMMAND="superfcgi --workers 1 --threads 1 --app trac_wsgi.app --socket $SOCKET_FILE --path /web/pybb.org"
source /home/web/bin/spawn.sh

Работа с биржами ссылок через библиотеку linkexchange

Меня попросили написать, как использовать библиотеку linkexchange для отображения сапоссылок. Я небуду теоретизировать особо — впрочем, как и всегда — и просто напишу голую техническую информацию.

Для работы с linxechange нам нужны две библиотеки. Сам linkexchange и phpserialize. Здесь я уже описывал способ, которым сейчас деплою сайты. Так вот в build/pipreq.txt у меня две строчки:

-e svn+http://svn.linkexchange.org.ua/LinkExchange/trunk#egg=LinkExchange
-e hg+http://dev.pocoo.org/hg/phpserialize-main#egg=phpserialize

Далее, идём в settings.py. Добавляем в TEMPLATE_CONTEXT_PROCESSORS строку 'linkexchange.django.context_processors.linkexchange'

Далее добавляем в settings.py настройки для работы с биржами ссылок:

LINKEXCHANGE_CLIENTS = [
    ('sape', [], {
        'user': '*******',
        'db_driver':  ('shelve', [], {
                       'filename': os.path.join(PROJECT_ROOT, 'var', 'sape.db')}), }),
    ('linkfeed', [], {
        'user': '*****',
        'db_driver': ('shelve', [], {
                      'filename': os.path.join(PROJECT_ROOT, 'var', 'linkfeed.db')}),
    }),
]

LINKEXCHANGE_FORMATTERS = [('list', [None], {})]

Что всё это значит, читайте на официальном сайте проекта. Я просто описываю быстрый старт :) Всё. Теперь у нас в контексте появляется переменная linkexchange_blocks — там ссылки :)

Выводить можно так:

<div class="advertize">
    {{ linkexchange_blocks.0|safe }}
</div>

Не забудьте создать директорию var и настроить права доступа к ней.