<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><title>Светлячный Dev Лог</title><link href="http://dev.svetlyak.ru" rel="alternate" /><id>http://dev.svetlyak.ru</id><updated>2011-12-26T03:42:00Z</updated><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/dev-svetlyak/all-ru" /><feedburner:info uri="dev-svetlyak/all-ru" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry><title>Исправление работы django-kombu с InnoDB</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/IVckvmVkv9g/" rel="alternate" /><updated>2011-12-26T03:42:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/kombu-fix/</id><summary type="html">&lt;p&gt;Наверное многие из вас используют &lt;a href="https://github.com/ask/django-celery"&gt;django-celery&lt;/a&gt;, или хотя бы слышали про этот
инструмент для асинхронного выполнения задач. В самом простейшем случае, когда
нет желания взводить дополнительные redis или AMQP сервера, очередь задач
организуется через уже имеющуюся базу данных. В моем случае это был MySQL.&lt;/p&gt;
&lt;p&gt;В случае, когда очередь организуется через базу, &lt;a href="https://github.com/ask/celery"&gt;Celery&lt;/a&gt; использует специальный
бэкенд &lt;a href="https://github.com/ask/django-kombu"&gt;django-kombu&lt;/a&gt;, который устроен довольно просто — при добавлении сообщения, оно
сериализуется и кладется в табличку БД, при операции pop, запись из базы изымается.
Такой вот AMQP для бедных.&lt;/p&gt;
&lt;p&gt;&lt;img alt="by weelakeo@flickr" src="http://farm3.staticflickr.com/2521/3815108314_8a67832b89.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Итак, я использовал MySQL и &lt;a href="https://github.com/ask/celery"&gt;celery&lt;/a&gt; работал отлично до тех пор, пока не было принято решение
сконвертировать все таблички в InnoDB дабы использовать транзакции.
После такого изменения, celery перестал "подхватывать" новые задачи и выполнял их только после рестарта.&lt;/p&gt;
&lt;p&gt;В интернетах предлагалось три решения этой проблемы:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;откатить все таблички &lt;a href="https://github.com/ask/django-kombu"&gt;django-kombu&lt;/a&gt; обратно на движок MyISAM;&lt;/li&gt;
&lt;li&gt;установить в конфиге MySQL &lt;code&gt;ISOLATION LEVEL READ-COMMITTED&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;раскомментировать некую строчку в сорцах &lt;a href="https://github.com/ask/django-kombu"&gt;django-kombu&lt;/a&gt;, после чего он станет переоткрывать
    коннекты к базе при каждой операции.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Объясню, почему все три никуда не годятся.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Вариант, предлагающий держать часть табличек в MyISAM, лишает нас транзакционности.&lt;/li&gt;
&lt;li&gt;Установка иного &lt;code&gt;ISOLATION LEVEL&lt;/code&gt;, глобально или в рамках сессии, может привести к некоторым
   "спецэффектам" при выполнении задач, рассчитанных на более строгую изоляцию.&lt;/li&gt;
&lt;li&gt;Переустановка соединения с базой каждый раз — просто грязный хак, замедляющий работу очереди
   в несколько раз.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Пришлось лезть в кишки django-kombu и править все самому. Оказалось, что по время опроса очереди,
библиотека держит коннект с базой открытым и все селекты выполняются в рамках одной длиииной трензакции.
Это означает, что все сообщения, которые будут добавлены в очередь в процессе, kombu не увидит
из за того, что в MySQL по умолчанию установлен &lt;code&gt;ISOLATION LEVEL REPEATABLE READ&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;В итоге, я обернул несколько методов в декоратор commit_on_success, и всё заработало как надо. При этом
небольшой тест показал производительность в 3 раза больше чем у версии с "грязным" хаком.&lt;/p&gt;
&lt;p&gt;Попутно поправил еще один баг, связанный с тем, что celery завершала свою работу в случае, когда MySQL
становился недоступен.&lt;/p&gt;
&lt;p&gt;Создал &lt;a href="https://github.com/ask/django-kombu/pull/9"&gt;Pull Request&lt;/a&gt;, но пока его не смерджили, исправления в &lt;a href="https://github.com/svetlyak40wt/django-kombu/commits/durability"&gt;моей ветке на GitHub&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/IVckvmVkv9g" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/kombu-fix/</feedburner:origLink></entry><entry><title>GitHub в Цифрах</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/9NaMuybm-yo/" rel="alternate" /><updated>2011-10-15T09:20:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/github-stats/</id><summary type="html">&lt;p&gt;Некоторое время назад мне в голову пришла идея сделать &lt;a href="http://dev.svetlyak.ru/github-trends/"&gt;сервис вокруг гитхаба&lt;/a&gt;. В первую очередь, он должен быть полезен тем, у кого много репозиториев и тем, кто следит за большим количеством кодеров или репозиториев.&lt;/p&gt;
&lt;p&gt;Основной паттерн моего использования социальных возможностей гитхаба таков: я фолловлю других кодеров для того, чтобы видеть в ленте какие проекты он находят, комментят и коммитят. Таким образом, я периодически обнаруживаю что-нибудь интересненькое. Вот только работало это лишь до определенного момента — пока объем данных в ленте не вырос настолько, что я стал лениться ее читать.&lt;/p&gt;
&lt;p&gt;Тут то и возникла идея — как-то агрегировать данные ленты, чтобы вычленять из нее интересные проекты. Например учитывая возросший к ним интерес людей из твоего первого или второго круга.&lt;/p&gt;
&lt;p&gt;Кроме того, сервис должен будет упростить отслеживание того, что происходит в форках ваших репозиториев. Это уже будет полезно тем, у кого десятки публичных репозиториев. Такая функциональность у меня даже реализована, но в виде простенького скрипта, который генерирует RSS с новыми коммитами в форках.&lt;/p&gt;
&lt;p&gt;А еще, можно сделать пузомерку для гиков, с медальками, рейтингами и титулами. Это просто прикольно: сделал десяток коммитов в полночь получи титул "Midnight code warrior" :)&lt;/p&gt;
&lt;p&gt;Однако, прежде чем ввязываться в затратное дело — создание отдельного сервиса для всех, я решил посчитать некоторую статистику отностиельно того, сколько же людей потенциально смогут оценить достоинства этого моего сервиса. Очень уж я не люблю делать бесполезные штуки :)&lt;/p&gt;
&lt;h2&gt;Прогресс&lt;/h2&gt;
&lt;p&gt;Для начала, я написал выкачивалку всех гитхаб пользователей. Что она делает. Она берет указанный логин, сохраняет его профиль, выкачивает список тех, кого за кем он следит, и добавляет их в очередь, если их профили еще не скачаны. Затем все повторяется для каждого логина, который в очереди, до тех пор, пока очередь не опустеет.&lt;/p&gt;
&lt;p&gt;Поскольку у API гитхаба есть ограничение в 5000 запросов в час, то процесс выкачивания всего графа затянулся почти на два дня.&lt;/p&gt;
&lt;p&gt;Всего было выкачано &lt;strong&gt;57 тысяч&lt;/strong&gt; профилей пользователей и около &lt;strong&gt;500 тысяч&lt;/strong&gt; репозиториев. На мой взгляд, пользователей маловато, но позможно так оно и есть. Раз мой скрипт до кого-то не добрался, значит его никто не фоловит, так что аккаунт скорее всего заброшен.&lt;/p&gt;
&lt;p&gt;Так выглядел процесс выкачивания. По этому графику хорошо заметен момент, когда очередь "на закачку" перестала расти и начала убывать.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt="" src="http://chart.apis.google.com/chart?cht=lxy&amp;amp;chs=800x300&amp;amp;chd=e:AAApBSB7CkDND2EfFIFxGaHDHrIUI9JmKPK4LhMKMzNcOFOuPXQAQpRSR7SkTNT1UeVHVwWZXCXrYUY9ZmaPa4bhcKczdceFeufXgAgohRh6ijjMj1kelHlwmZnCnroUo9pmqPq4rhsKsztbuEutvWv.woxRx6yjzMz10e1H1w2Z3C3r4U495m6O637g8J8y9b-E-t.W..,FtSbbKjQmuqvthvwxU0D043Z5o5d7N-I8X8K9B9X-V...e.8-z-z.Y.o.A-K-J9w736D7Q6C425S3P23000dzuyMy-y2ySy1xyw6wcuyuFtFrQrAqGpPphomnNmpnsmQmSkhjVh9hTfselfjecczeVcPbfavZMcRbUaIYgXnWwV4UyUTSCRLPIN4MaKrJJHpGIEzDSCJAH&amp;amp;chtt=%D0%9F%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%20%D0%B2%D1%8B%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;amp;chxt=y,x,y,x&amp;amp;chxl=2:%7c%D0%BE%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D1%8C%7c3:%7c%D0%BA%D0%BE%D0%BB-%D0%B2%D0%BE%20%D0%B2%D1%8B%D0%BA%D0%B0%D1%87%D0%B0%D0%BD%D1%8B%D1%85%20%D0%BF%D1%80%D0%BE%D1%84%D0%B8%D0%BB%D0%B5%D0%B9&amp;amp;chxr=0,0,3707%7c1,0,57001&amp;amp;chxp=2,50%7c3,50&amp;amp;chg=25,25,1,0" /&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;Наблюдай и следи&lt;/h2&gt;
&lt;p&gt;Самое важное для моего проекта — доли пользователей, которые наблюдают за большим количеством репозиториев или следят за большим количеством других разработчиков:&lt;/p&gt;
&lt;center&gt;

&lt;p&gt;&lt;img alt="" src="http://chart.apis.google.com/chart?cht=p3&amp;amp;chs=500x150&amp;amp;chd=s:EbZAFA&amp;amp;chtt=%D0%A1%D0%BB%D0%B5%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B7%D0%B0%20%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F%D0%BC%D0%B8&amp;amp;chco=208020&amp;amp;chl=%3C1000%20%287.0%25%29%7c%3C100%20%2843.7%25%29%7c%3C10%20%2841.7%25%29%7c%3C10000%20%2852%29%7cnot%20watching%20%287.4%25%29%7c%3E10000%20%281%29" /&gt; | &lt;img alt="" src="http://chart.apis.google.com/chart?cht=p3&amp;amp;chs=500x150&amp;amp;chd=s:ANdT&amp;amp;chtt=%D0%9A%D0%BE%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D1%82%D0%B2%D0%BE%20%D0%B7%D0%B0%D1%84%D0%BE%D0%BB%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D1%8B%D1%85&amp;amp;chco=208020&amp;amp;chl=%3E100%20%28410%29%7c%3C100%20%2820.5%25%29%7c%3C10%20%2847.3%25%29%7cnot%20following%20%2831.4%25%29" /&gt;&lt;/p&gt;
&lt;/center&gt;

&lt;p&gt;Отсюда видно, что почти 50% пользователей, следят более чем за 10 репозиториями и 7% (около 4 тысяч) — более чем за 100. Так же мне интересны те 20% (11 тысяч), что фоловят более 10 человек, они наверняка не успевают прочитывать весь свой News Feed. И уж наверняка оставшиеся 410 пользователей, что фоловят больше сотни по достоинству оценят аггрегатор новостной ленты. Сам я, как раз вхожу в последнюю категорию, так как фоловлю 331 разработчика.&lt;/p&gt;
&lt;h2&gt;Количество репозиториев&lt;/h2&gt;
&lt;p&gt;Среднее количество публичных репозиториев у гитхаб пользователя — &lt;strong&gt;8&lt;/strong&gt;, из них &lt;strong&gt;3&lt;/strong&gt;, это форки чужих проектов.&lt;/p&gt;
&lt;center&gt;

&lt;p&gt;&lt;img alt="" src="http://chart.apis.google.com/chart?cht=p3&amp;amp;chs=500x150&amp;amp;chd=s:PlJA&amp;amp;chtt=%D0%9A%D0%BE%D0%BB-%D0%B2%D0%BE%20%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D1%87%D0%BD%D1%8B%D1%85%20%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B5%D0%B2&amp;amp;chco=208020&amp;amp;chl=%3C100%20%2824.4%25%29%7c%3C10%20%2860.1%25%29%7cno%20reps%20%2815.2%25%29%7c%3E100%20%28158%29" /&gt; | &lt;img alt="" src="http://chart.apis.google.com/chart?cht=p3&amp;amp;chs=500x150&amp;amp;chd=s:mIPA&amp;amp;chtt=%D0%9A%D0%BE%D0%BB-%D0%B2%D0%BE%20%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D1%87%D0%BD%D1%8B%D1%85%20%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B5%D0%B2%20%28%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B0%D1%8F%20%D1%84%D0%BE%D1%80%D0%BA%D0%B8%29&amp;amp;chco=208020&amp;amp;chl=%3C10%20%2862.1%25%29%7c%3C100%20%2813.4%25%29%7cno%20reps%20%2824.4%25%29%7c%3E100%20%2854%29" /&gt;&lt;/p&gt;
&lt;/center&gt;

&lt;p&gt;На этом графике видно, что 60% пользователей имеют меньше 10 публичных репозиториев, а у 15% их нет вообще. Зато почти у четверти пользователей гитхаба публичных репозиториев от 10-100, и они являются потенциальными пользователями моего сервиса.&lt;/p&gt;
&lt;p&gt;При этом, процент активных репозиториев, таких в которые был хотя бы один push за последний месяц, всего 10%.&lt;/p&gt;
&lt;h2&gt;И еще…&lt;/h2&gt;
&lt;p&gt;Всего я выкачал информацию чуть больше чем по &lt;strong&gt;500 тысячам&lt;/strong&gt; репозиториев, и примерно &lt;strong&gt;40%&lt;/strong&gt; из них, это форки. Удивительно, я думал что форков должно быть гораздо гораздо больше.&lt;/p&gt;
&lt;p&gt;Посчитал я и то, насколько больше "второй круг", по сравнению с первым.
В среднем, пользователь гитхаба фоловит 9 человек, и следит за 33 репозиториями, это его "первый круг". Второй круг, это те, кого фоловят те, кого фоловят ты. Второй круг, уже составляет 230 пользователей и 800 репозиториев. И это в среднем. Для нерепрезентативных вроде меня цифры другие: 11548 и 42882 соответственно. Это почти 1/5 всего гитхаба!&lt;/p&gt;
&lt;p&gt;Кроме этого, я посчитал соотношение &lt;strong&gt;обычных пользователей&lt;/strong&gt;  и &lt;strong&gt;организаций&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
&lt;img alt="" src="http://chart.apis.google.com/chart?cht=p3&amp;amp;chs=500x150&amp;amp;chd=s:7C&amp;amp;chtt=%D0%A2%D0%B8%D0%BF%D1%8B%20%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D0%B5%D0%B9&amp;amp;chco=208020&amp;amp;chl=User%20%2896.41%25%29%7cOrganization%20%283.59%25%29" /&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;Немножко Топов, я знаю, они всем нравятся :)&lt;/h2&gt;
&lt;style&gt;
  div.top {width: 40%; float: left; margin-right: 10%;}
  br.clear {border: 0px; clear: both;}
&lt;/style&gt;

&lt;div class="top"&gt;

&lt;h3 id="top_20_++++++++"&gt;Top 20 компаний&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Компания&lt;/th&gt;
&lt;th&gt;Кол-во&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td /&gt;
&lt;td&gt;37965&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ThoughtWorks&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mozilla&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Red Hat&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Freelance&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Twitter&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Japan&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yandex&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Freelancer&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Globo.com&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yahoo!&lt;/td&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intridea&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facebook&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Student&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emergya&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pivotal Labs&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engine Yard&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;div class="top"&gt;

&lt;h3 id="top_20_+++++++"&gt;Top 20 городов&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Город&lt;/th&gt;
&lt;th&gt;Кол-во&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td /&gt;
&lt;td&gt;23657&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;San Francisco&lt;/td&gt;
&lt;td&gt;1441&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;London&lt;/td&gt;
&lt;td&gt;962&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New York&lt;/td&gt;
&lt;td&gt;578&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paris&lt;/td&gt;
&lt;td&gt;474&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chicago&lt;/td&gt;
&lt;td&gt;458&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seattle&lt;/td&gt;
&lt;td&gt;457&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tokyo&lt;/td&gt;
&lt;td&gt;430&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Berlin&lt;/td&gt;
&lt;td&gt;423&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Germany&lt;/td&gt;
&lt;td&gt;417&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portland&lt;/td&gt;
&lt;td&gt;346&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toronto&lt;/td&gt;
&lt;td&gt;317&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boston&lt;/td&gt;
&lt;td&gt;288&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Austin&lt;/td&gt;
&lt;td&gt;280&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sydney&lt;/td&gt;
&lt;td&gt;272&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stockholm&lt;/td&gt;
&lt;td&gt;261&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Japan&lt;/td&gt;
&lt;td&gt;244&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Los Angeles&lt;/td&gt;
&lt;td&gt;230&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brooklyn&lt;/td&gt;
&lt;td&gt;226&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Melbourne&lt;/td&gt;
&lt;td&gt;221&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;div class="top"&gt;

&lt;h3 id="top_20_++++++++++++++"&gt;Top 20 "последователей"&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Логин&lt;/th&gt;
&lt;th&gt;Фолловит&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;snytkine&lt;/td&gt;
&lt;td&gt;3242&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mtsoerin&lt;/td&gt;
&lt;td&gt;1983&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;webiest&lt;/td&gt;
&lt;td&gt;1903&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;superfeedr&lt;/td&gt;
&lt;td&gt;1710&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;charlenopires&lt;/td&gt;
&lt;td&gt;1236&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stonegao&lt;/td&gt;
&lt;td&gt;1205&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marak&lt;/td&gt;
&lt;td&gt;1068&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;speedygonzalez&lt;/td&gt;
&lt;td&gt;1059&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tyru&lt;/td&gt;
&lt;td&gt;1022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;esneko&lt;/td&gt;
&lt;td&gt;867&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;josegonzalez&lt;/td&gt;
&lt;td&gt;640&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c9s&lt;/td&gt;
&lt;td&gt;556&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kanzure&lt;/td&gt;
&lt;td&gt;555&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;take-cheeze&lt;/td&gt;
&lt;td&gt;517&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;elliottcable&lt;/td&gt;
&lt;td&gt;495&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sannis&lt;/td&gt;
&lt;td&gt;475&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mattn&lt;/td&gt;
&lt;td&gt;462&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;j2labs&lt;/td&gt;
&lt;td&gt;453&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dpree&lt;/td&gt;
&lt;td&gt;446&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rkh&lt;/td&gt;
&lt;td&gt;444&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;div class="top"&gt;

&lt;h3 id="top_20_++++_++++++++"&gt;Top 20 "кого фолловят"&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Логин&lt;/th&gt;
&lt;th&gt;Последователей&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;defunkt&lt;/td&gt;
&lt;td&gt;4005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;torvalds&lt;/td&gt;
&lt;td&gt;3803&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jeresig&lt;/td&gt;
&lt;td&gt;3466&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mojombo&lt;/td&gt;
&lt;td&gt;3248&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ryanb&lt;/td&gt;
&lt;td&gt;2737&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;schacon&lt;/td&gt;
&lt;td&gt;2429&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;paulirish&lt;/td&gt;
&lt;td&gt;2316&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dhh&lt;/td&gt;
&lt;td&gt;2170&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wycats&lt;/td&gt;
&lt;td&gt;2044&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ry&lt;/td&gt;
&lt;td&gt;2032&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rails&lt;/td&gt;
&lt;td&gt;1946&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;facebook&lt;/td&gt;
&lt;td&gt;1802&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jquery&lt;/td&gt;
&lt;td&gt;1767&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;technoweenie&lt;/td&gt;
&lt;td&gt;1572&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pjhyett&lt;/td&gt;
&lt;td&gt;1563&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;visionmedia&lt;/td&gt;
&lt;td&gt;1554&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cyanogen&lt;/td&gt;
&lt;td&gt;1410&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;douglascrockford&lt;/td&gt;
&lt;td&gt;1380&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tpope&lt;/td&gt;
&lt;td&gt;1369&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;android&lt;/td&gt;
&lt;td&gt;1317&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;div class="top"&gt;

&lt;h3 id="top_20_++_++++++++++++"&gt;Top 20 "по репозиториям"&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Логин&lt;/th&gt;
&lt;th&gt;Репозиториев&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gitpan&lt;/td&gt;
&lt;td&gt;21976&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vim-scripts&lt;/td&gt;
&lt;td&gt;3735&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;emacsmirror&lt;/td&gt;
&lt;td&gt;3101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Epictetus&lt;/td&gt;
&lt;td&gt;911&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;panega&lt;/td&gt;
&lt;td&gt;612&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jenkinsci&lt;/td&gt;
&lt;td&gt;602&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dev2dev&lt;/td&gt;
&lt;td&gt;504&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wave2future&lt;/td&gt;
&lt;td&gt;411&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CyanogenMod&lt;/td&gt;
&lt;td&gt;342&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MechanisM&lt;/td&gt;
&lt;td&gt;329&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rjbs&lt;/td&gt;
&lt;td&gt;325&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tokuhirom&lt;/td&gt;
&lt;td&gt;297&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rwldrn&lt;/td&gt;
&lt;td&gt;297&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aculich&lt;/td&gt;
&lt;td&gt;287&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rainly&lt;/td&gt;
&lt;td&gt;282&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;albertobraschi&lt;/td&gt;
&lt;td&gt;278&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;idega&lt;/td&gt;
&lt;td&gt;272&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rafl&lt;/td&gt;
&lt;td&gt;266&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apache&lt;/td&gt;
&lt;td&gt;258&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kristianmandrup&lt;/td&gt;
&lt;td&gt;244&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;div class="top"&gt;

&lt;h3 id="top_20_++++++++_1"&gt;Top 20 "вотчеров"&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Логин&lt;/th&gt;
&lt;th&gt;Наблюдает за&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gitpan&lt;/td&gt;
&lt;td&gt;21976&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vim-scripts&lt;/td&gt;
&lt;td&gt;3736&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;emacsmirror&lt;/td&gt;
&lt;td&gt;3588&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stonegao&lt;/td&gt;
&lt;td&gt;2789&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;abecciu&lt;/td&gt;
&lt;td&gt;2474&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;igrigorik&lt;/td&gt;
&lt;td&gt;2415&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;charlenopires&lt;/td&gt;
&lt;td&gt;2339&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stan&lt;/td&gt;
&lt;td&gt;2318&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;matagus&lt;/td&gt;
&lt;td&gt;2160&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;smtlaissezfaire&lt;/td&gt;
&lt;td&gt;1955&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rmetzler&lt;/td&gt;
&lt;td&gt;1916&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;shanlalit&lt;/td&gt;
&lt;td&gt;1897&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;willi&lt;/td&gt;
&lt;td&gt;1896&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Epictetus&lt;/td&gt;
&lt;td&gt;1821&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;filipeamoreira&lt;/td&gt;
&lt;td&gt;1812&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;arden&lt;/td&gt;
&lt;td&gt;1783&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;andrew&lt;/td&gt;
&lt;td&gt;1746&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;methodmissing&lt;/td&gt;
&lt;td&gt;1665&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rkh&lt;/td&gt;
&lt;td&gt;1571&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lgs&lt;/td&gt;
&lt;td&gt;1511&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;h2&gt;Всё&lt;/h2&gt;
&lt;p&gt;Наверняка по той базе, что у меня теперь есть, можно посчитать еще что-нибудь интересненькое. Но пока это всё. Будут идеи — присылайте.&lt;/p&gt;
&lt;p&gt;P.S. — думаю, проектик найдет свою аудиторию в несколько тысяч человек. Осталось придумать, как его монетизировать, чтобы окупать аренду серверных мощностей.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/9NaMuybm-yo" height="1" width="1"/&gt;</summary><category term="git" /><category term="github" /><feedburner:origLink>http://dev.svetlyak.ru/github-stats/</feedburner:origLink></entry><entry><title>For-Else in Python</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/VlGdtjSuZQ4/" rel="alternate" /><updated>2011-04-18T11:18:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/for-else/</id><summary type="html">&lt;p&gt;Пользовались ли вы когда-нибудь ключевым словом &lt;a href="http://docs.python.org/reference/compound_stmts.html#the-for-statement"&gt;&lt;code&gt;else&lt;/code&gt;&lt;/a&gt;, совместно с &lt;code&gt;for&lt;/code&gt;? Нет? Мне тоже не приходилось.
А оказывается, это чрезвычайно удобно в тех случаях, когда в теле цикла используется ключевое слово &lt;code&gt;break&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Как работает &lt;code&gt;else&lt;/code&gt;? Очень просто. Код этой секции выполняется в том случае, если основной цикл завершился
естественным образом, без эксепшена или вызова &lt;code&gt;break&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;То есть, это ключевое слово будет полезно тогда, когда вам нужно выполнить какой-то код только в том случае,
если в цикле были перебраны все элементы. Я на этом сделал аналог &lt;code&gt;switch&lt;/code&gt; с поддержкой регекспов и значением
по-умолчанию:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;REGEXES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;some ([^ ]+)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;do_some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;another ([^ ]+)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;do_another&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;REGEXES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;REGEXES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_to_check&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;regexes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;regexes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_to_check&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;blah minor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REGEXES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;do_default_action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/VlGdtjSuZQ4" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/for-else/</feedburner:origLink></entry><entry><title>Git helper for GitHub Users</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/Dm0ceqbUZ-0/" rel="alternate" /><updated>2011-04-14T12:26:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/hub/</id><summary type="html">&lt;p&gt;Нашел тут &lt;a href="http://defunkt.io/hub/"&gt;скриптик&lt;/a&gt;, облегчающий работу с гитхабом из командной строки. Все что нужно, это иметь установленный ruby (в Mac OS X, он уже есть). Скрипт достаточно скачать куда-нибудь в &lt;code&gt;PATH&lt;/code&gt;, и сделать на него алиас &lt;code&gt;alias git=hub&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Для полноценной работы, нужно так же добавить в &lt;code&gt;~/.gitconfig&lt;/code&gt; секцию с вашим именем и токеном:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[github]&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;svetlyak40wt&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;а-тут-секретный-токен&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;После этого можно будет, к примеру, по-быстрому форкнуть мой проект &lt;a href="http://github.com/svetlyak40wt/forkfeed"&gt;ForkFeed&lt;/a&gt;, который, кстати, тоже полезен пользователям GitHub:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;clone&lt;/span&gt; &lt;span class="n"&gt;svetlyak40wt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;forkfeed&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;forkfeed&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="nb"&gt;fork&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Наслаждайтесь!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/Dm0ceqbUZ-0" height="1" width="1"/&gt;</summary><category term="git" /><feedburner:origLink>http://dev.svetlyak.ru/hub/</feedburner:origLink></entry><entry><title>Github Trends</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/PClDKRMVq5A/" rel="alternate" /><updated>2011-04-05T10:29:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/github-trends/</id><summary type="html">&lt;p&gt;Знаете, какой мой основной паттерн использования GitHub? Помимо хранения в нем кода своих проектиков, конечно.
Я подписан на множество людей, которые постоянно вотчат разные репозитории. И у меня есть RSS фид, в котором
только информация о том, кто чем заинтересовался. Периодически я этот фид просматриваю, и если вижу, что
у какого-то репозитория появилось много вотчеров, то присматриваюсь к проекту поближе.&lt;/p&gt;
&lt;p&gt;И вот вчера меня посетила идея, сделать проект, показывающий рост интереса пользователей к тем или иным проектам
на GitHub. Но прежде чем браться за реализацию, я пошел погуглил и оказалось, что:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;во-первых, такая &lt;a href="https://github.com/languages/Python"&gt;информация уже есть&lt;/a&gt; на самом GitHub, но за ней совершенно неудобно следить, так как нет подписки;&lt;/li&gt;
&lt;li&gt;во-вторых, есть &lt;a href="https://github.com/oscardelben/github-trends/"&gt;проект&lt;/a&gt;, который превращает гитхабовскую информацию в RSSки.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Однако, все же это немного не то, чего бы мне хотелось. Ведь гитхаб показывает просто топ самых популярных проектов,
а хотелось бы видеть кривую роста популярности для каждого из проектов.&lt;/p&gt;
&lt;p&gt;Тем не менее, пока не буду писать свой велосипед, и попробую воспользоваться &lt;a href="3/"&gt;GitHub Trends&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/PClDKRMVq5A" height="1" width="1"/&gt;</summary><category term="git" /><category term="github" /><feedburner:origLink>http://dev.svetlyak.ru/github-trends/</feedburner:origLink></entry><entry><title>Асинхронное тестирование с Twisted</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/PNtxCtKWLxo/" rel="alternate" /><updated>2011-03-01T10:53:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/async-testing/</id><summary type="html">&lt;p&gt;Недавно &lt;a href="http://twitter.com/svetlyak40wt/status/38659219838337024"&gt;я писал&lt;/a&gt; в твиттере, что мне нравится писать юниттесты для программ, использующих Python фреймворк &lt;a href="http://twistedmatrix.com"&gt;Twisted&lt;/a&gt;. В твиттере всего не уместишь, поэтому я решил оформить пояснение отдельным постом.&lt;/p&gt;
&lt;p&gt;Дело в том, что я сейчас разрабатываю распределенный, отказоустойчивый сервис локов. Внутри он асинхронный, использует Twisted. Ноды общаются между собой по TCP, используя собственный текстовый протокол, а наружу предоставляют HTTP интерфейс в духе REST. Мне очень важно обеспечить стабильность и консистентность моего сервиса, потому что от него будет зависеть синхронизация других задач. Поэтому первое что я сделал, это написал тесты. Эти тесты даже и unit-тестами назвать нельзя, они скорее, функциональные.&lt;/p&gt;
&lt;p&gt;Итак, мои тесты тестируют взаимодействие нескольких нод. Ноды обмениваются данными по сети. Но как такой тест запускать? Стартовать ли несколько процессов? И если да, то как их отлаживать?&lt;/p&gt;
&lt;p&gt;Отладка сетевого взаимодействия сложная штука. Но тут (барабанная дробь), на сцену выходит Twisted!&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://img-fotki.yandex.ru/get/5005/alexander-artemenko.e/0_755a1_e15d9963_L" /&gt;&lt;br /&gt;
by &lt;a href="http://www.flickr.com/photos/dirkscircusimages/2785357852/"&gt;dirkjanranzijn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Во-первых, твистед позволил мне запустить все мои пять нод в одном процессе, на одном реакторе. Это позволило ставить &lt;code&gt;pdb.set_trace()&lt;/code&gt; в любом месте, где мне нужен отладчик.&lt;/p&gt;
&lt;p&gt;Во-вторых, я заменил сетевой слой, который они используют для общения, на его упрощенную модель, где сообщения доставляются с некоторой случайной задержкой. Так как генератором случайных чисел можно управлять, то я получил возможность воспроизводить разные "псевдо-случайные" глюки, происходящие в моей системе.  Вот как выглядит процесс тестирования (все происходит автоматически):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;перед запуском теста, &lt;code&gt;TestCase&lt;/code&gt; класс вызывает &lt;code&gt;random.seed(some_value)&lt;/code&gt; и запоминает значение &lt;code&gt;some_value&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;если тест сломался, то помимо обычного сообщения об ошибке, &lt;code&gt;TestCase&lt;/code&gt; выводит &lt;code&gt;RANDOM SEED: some_value&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;чтобы запустить тест, явно инициализировав генератор случайных чисел, я его декорирую:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nd"&gt;@seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_my_broken_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Вот класс &lt;code&gt;TestCase&lt;/code&gt; и декоратор &lt;code&gt;seed&lt;/code&gt;, реализующие данную функциональность:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twisted.trial&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_random_seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;decorator&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;_random_seed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;seed_info_adder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; (random seed: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;)&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;

        &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addErrback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed_info_adder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Что думаете по поводу такого метода отладки? &lt;span style="color: white;"&gt;13558447.88548999.1298975203.fe8d17be47add1425588f6f16d9ae6e9 и 1120000000000244.1354.1298974870.96d80d1dda087b5185ebddd707442782&lt;/span&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/PNtxCtKWLxo" height="1" width="1"/&gt;</summary><category term="python" /><category term="twisted" /><feedburner:origLink>http://dev.svetlyak.ru/async-testing/</feedburner:origLink></entry><entry><title>Еще одна HTTP либа</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/JHiXUFymU3U/" rel="alternate" /><updated>2011-02-16T11:11:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/yet-another-http/</id><summary type="html">&lt;p&gt;Порой в твиттере прилетают интересные ссылки. Так, например, вчера Юрий Юревич заретвитил несколько обновлений с PyPi. Одно из них — новая &lt;a href="http://pypi.python.org/pypi/requests/"&gt;библиотечка для работы с HTTP&lt;/a&gt;. По сути, это всего лишь обертка над стандартными urllib и urllib2, призванная сделать работу с ними проще и понятней. Однако, уже по дизайну этого велосипеда видно, что где его можно улучшить.&lt;/p&gt;
&lt;p&gt;Например, для того, чтобы использовать аутентификацию с именем пользователя и паролем, там предлагается сначала создать некий Auth объект и потом использовать его во всех вызовах:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;conv_auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;requeststest&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;requeststest&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://convore.com/api/account/verify.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conv_auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Зачем? Если этот объект только и делает, что хранит имя пользователя и пароль, почему не использовать вместо него tuple (username, password)??? Если уж делаешь просто, почем не идешь до конца?&lt;/p&gt;
&lt;p&gt;И еще пара замечаний по коду. Использовать с python2.5 его нельзя, потому что автор забыл про &lt;code&gt;from __future__ import absolute_import&lt;/code&gt;, try except блоки тоже newstyle, в главном методе, посылающем запрос, чудовищный ifelse блок на два экрана. И еще, он пытается сам вызывать манкипатчинг, если присутствуют библиотеки eventlet или gevent. Что будет, если у меня они установлены в системе, но я не собираюсь их использовать?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/JHiXUFymU3U" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/yet-another-http/</feedburner:origLink></entry><entry><title>Zen для каждого</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/JajNr9mB3HY/" rel="alternate" /><updated>2011-02-15T10:03:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/zen-for-everyone/</id><summary type="html">&lt;p&gt;Есть такой &lt;a href="http://www.python.org/dev/peps/pep-0020/"&gt;PEP-0020&lt;/a&gt;, с которым рекомендуется ознакомиться всем начинающим питонистам как только они изучат &lt;a href="http://www.python.org/dev/peps/pep-0008/"&gt;PEP-0008&lt;/a&gt;. Это краткий свод правил, которых следует придерживаться, программируя на питоне (да и не только не нем, кстати). Правила составил Тим Петерс и они, кстати, доступны прямо из интерпретатора. Достаточно сделать &lt;code&gt;import this&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Но сегодня я хочу дать ссылку на &lt;a href="http://artifex.org/~hblanks/talks/2011/pep20_by_example.html"&gt;приложение к PEP-0020&lt;/a&gt;, написанное Хантером Бланксом. В нем он приводит краткие иллюстрации каждого пункта из "The Zen of Python". Получилось очень наглядно.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/JajNr9mB3HY" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/zen-for-everyone/</feedburner:origLink></entry><entry><title>Эволюция проектов на GitHub</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/gQRPNXxam-s/" rel="alternate" /><updated>2011-02-09T12:11:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/forkfeed/</id><summary type="html">&lt;p&gt;Сегодня зарелизил небольшую но очень полезную тулзу для пользователей GitHub. Проект называется &lt;a href="https://github.com/svetlyak40wt/forkfeed"&gt;Forkfeed&lt;/a&gt;. Он предназначен для слежения за развитием форков ваших проектов на гитхабе.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://img-fotki.yandex.ru/get/5602/alexander-artemenko.e/0_73de3_97d804f1_L" /&gt;&lt;br /&gt;
by &lt;a href="http://www.flickr.com/photos/graeme_pow/4534266872/in/photostream/"&gt;GraemePow@flickr&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Часто бывает такое, что кто-то форкает ваш проект, начинает его развивать, а pull реквестов не присылает. А за всем ведь не уследишь. Поэтому я написал скрипт, который обходит все форки всех моих проектов, смотрит какие их коммиты новее моих, и создает Atom фид. &lt;a href="http://forkfeed.svetlyak.ru/svetlyak40wt.xml"&gt;Фид&lt;/a&gt; обновляется по крону, на моем VPS. Теперь я всегда буду в курсе новых форков каждого из моих проектов.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/gQRPNXxam-s" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/forkfeed/</feedburner:origLink></entry><entry><title>Кролик в Бутылке</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/Sn3-P4uaLRE/" rel="alternate" /><updated>2011-02-01T00:35:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/cony-in-the-bottle/</id><summary type="html">&lt;p&gt;Полторы недели назад, в заметке "&lt;a href="http://dev.svetlyak.ru/facebook-bunny1/"&gt;Командная строка для интернетов&lt;/a&gt;", я писал о довольно полезном сервисе умных закладок, которые каждый может дописать под свои нужды и запустить либо на локальной машине, либо на собственном сервере.&lt;/p&gt;
&lt;p&gt;Поковыряв код этого проекта, я пришел к выводу, что он для такой простой идеи, реализация чересчур сложна. И конечно же, решил все переписать :-)&lt;/p&gt;
&lt;p&gt;Теперь &lt;a href="http://github.com/svetlyak40wt/cony/"&gt;мой форк&lt;/a&gt; называется не Bunny, а Cony, что является синонимом слова кролик.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://img-fotki.yandex.ru/get/5800/alexander-artemenko.e/0_735d1_b947df73_L" /&gt;&lt;br /&gt;
by &lt;a href="http://flic.kr/p/6bLmMR"&gt;animalvegetable@flickr&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Если Bunny1 тянет за собой большой и толстый CherryPy, то Cony гораздо скромнее. Он основан на микро-фреймворке &lt;a href="http://github.com/defnull/bottle"&gt;Bottle&lt;/a&gt;, а Bottle, это всего лишь один файл, который лежит прямо в репозитории проекта.&lt;/p&gt;
&lt;h2&gt;Расширяй&lt;/h2&gt;
&lt;p&gt;Благодаря отсутствию зависимостей, установка Cony предельно проста — клонируем репозиторий, и запускаем &lt;code&gt;./cony.py&lt;/code&gt;. Дописывание своих шоткатов тоже предельно просто — достаточно положить в текущей директории файлик &lt;code&gt;local_commands.py&lt;/code&gt;. Готовые примеры некоторых команд лежат в директории &lt;code&gt;examples&lt;/code&gt;. Можно в начале &lt;code&gt;local_commands.py&lt;/code&gt; сделать &lt;code&gt;from examples import *&lt;/code&gt; чтобы подключить их все, либо импортировать только нужные функции.&lt;/p&gt;
&lt;h2&gt;Клонируй и делись&lt;/h2&gt;
&lt;p&gt;Если ты написал интересный шоткат, и считаешь, что он будет полезен другим — не ленись, &lt;a href="http://github.com/svetlyak40wt/cony/"&gt;форкни проект&lt;/a&gt; на GitHub, и добавь команду отдельным файлом в examples, а затем пришли Pull Request.&lt;/p&gt;
&lt;h2&gt;И немного про бутылку&lt;/h2&gt;
&lt;p&gt;Bottle мне вполне понравился. Очень минималистичный. В нем есть роутинг урлов и своя реализация шаблонов. Впрочем, шаблонизатор можно использовать любой, как и ORM. Для небольших проектиков вроде этого, когда нужно быстренько поднять сервер и сверстать пару страниц — самое оно.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/Sn3-P4uaLRE" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/cony-in-the-bottle/</feedburner:origLink></entry><entry><title>OpenSource Переводы</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/2jO2Uv3TfvI/" rel="alternate" /><updated>2011-01-27T14:55:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/transifex/</id><summary type="html">&lt;p&gt;Где-то неделю назад, в рассылке &lt;a href="http://groups.google.com/group/django-developers"&gt;django-developers&lt;/a&gt; пробежал анонс, что отныне работать над многочисленными переводами джанги, можно будет через онлайновый сервис переводов &lt;a href="http://www.transifex.net"&gt;Transifex&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Transifex, предоставляет интерфейс для работы над переводами. Процесс выглядит так:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Регистрируешься.&lt;/li&gt;
&lt;li&gt;Добавляешь свой проект.&lt;/li&gt;
&lt;li&gt;Указываешь какие у него есть PO файлы, загружая уже готовые переводы.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Дальше нужно чтобы вокруг проекта образовалось некоторое количество желающих его перевести. Они организуются в команды.&lt;/p&gt;
&lt;p&gt;После того, как перевод готов, можно скачать готовые PO файлы.&lt;/p&gt;
&lt;p&gt;Ради эксперимента, я добавил туда один свой небольшой проектик &lt;a href="http://www.transifex.net/projects/p/django-faces/"&gt;django-faces&lt;/a&gt;. Если знаете язык, отличный от английского и русского, попробуйте перевести. Строк для перевода совсем немного.&lt;/p&gt;
&lt;p&gt;Кстати, всего я насчитал на Transifex около 600 проектов, и лишь в половине из них есть ресурсы для перевода. Это значит, что разработчики начинают играться с интерфейсом, но что-то им мешает довести дело до конца. Интерфейс действительно местами очень непонятный. К примеру, я так и не смог понять, как поискать интересный проект, который бы можно было перевести на русский.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/2jO2Uv3TfvI" height="1" width="1"/&gt;</summary><category term="web" /><category term="tools" /><feedburner:origLink>http://dev.svetlyak.ru/transifex/</feedburner:origLink></entry><entry><title>Командная строка для Интернетов</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/F6EabkIeHgw/" rel="alternate" /><updated>2011-01-20T11:11:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/facebook-bunny1/</id><summary type="html">&lt;p&gt;Пользуетесь ли вы поисковыми сокращениями, позволяющими после шотката ввести запрос прямо в адресной строке браузера? Если да, то вам наверняка будет интересно узнать, что можно не только подключать к вашему браузеру готовые поисковые движки, но и писать более изощренные обработчики запросов прямо на Python.&lt;/p&gt;
&lt;p&gt;Вчера я отрыл на гитхабе интересный проект, разработанный когда-то в недрах Facebook. Проект носит странное название &lt;a href="https://github.com/facebook/bunny1/"&gt;Bunny1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://resizer.co/?image=http%3A%2F%2Ffarm1.static.flickr.com%2F120%2F256250194_ac05247901_o.jpg&amp;amp;w=600" /&gt;&lt;br /&gt;
by &lt;a href="http://www.flickr.com/photos/opid/256250194/"&gt;serenionion@flickr&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Bunny1, это вебсервер, который принимает команду и некоторый запрос, а затем либо редиректит куда-то, либо показывает HTML страницу с результатом. Если подключить URL этого сервера в качестве поиска по умолчанию, то вы получаете командную строку прямо в браузере. И самое полезное тут то, что:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;это будет работать во всех современных браузерах;&lt;/li&gt;
&lt;li&gt;команды можно дописывать самому, на python.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;К примеру, можно написать обработчик, который по запросу &lt;code&gt;tw Доброе утро Москва!&lt;/code&gt; будет твитить в ваш аккаунт и редиректить на страничку этого твита.&lt;/p&gt;
&lt;p&gt;Справедливости ради, стоит отметить, что идея не нова. Существует, к примеру, отдельный сервис &lt;a href="http://yubnub.org/"&gt;YubNub&lt;/a&gt;, реализующий эту функциональность, и плагин для Firefox &lt;a href="https://mozillalabs.com/ubiquity/"&gt;Ubiquity&lt;/a&gt;. Но первый содержит множество лишних команд и там нельзя дописать новые, а второй работает только под Firefox.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/F6EabkIeHgw" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/facebook-bunny1/</feedburner:origLink></entry><entry><title>Отладчик с блэкджеком</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/wAy_cc0PG5Y/" rel="alternate" /><updated>2011-01-18T14:13:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/debugger-with-blackjack/</id><summary type="html">&lt;p&gt;Я решил придерживаться определенного графика публикации постов. Попробую выкладывать что-нибудь интересное каждый вторник и четверг.&lt;/p&gt;
&lt;p&gt;Сегодня я хочу рассказать вам о довольно полезном инструменте, а именно — об отладчике. Если вы не любитель развесистых IDE, то наверняка пользовались стандартным отладчиком питона pdb. Он покрывает 90% необходимой мне функциональности, но все же, несколько неудобен. Например, в нем нет автокомплита и несколько напрягает необходимость периодически набирать команду для вывода листинга, чтобы посмотреть на контекст текущей строки.&lt;/p&gt;
&lt;p&gt;Эти, а так же некоторые другие проблемы, призван решить проект &lt;a href="https://bitbucket.org/antocuni/pdb/src"&gt;pdbpp&lt;/a&gt;. Вот несколько фич, которые оказались мне наиболее полезны:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sticky mode — в этом режиме отладчик перерисовывает текущую функцию после каждого шага интерпретатора;&lt;/li&gt;
&lt;li&gt;автокомплит по табу — больше не надо вызывать &lt;code&gt;dir(obj)&lt;/code&gt; чтобы ознакомиться с интерфейсом объекта;&lt;/li&gt;
&lt;li&gt;подсветка синтаксиса — pdbpp использует Pygments, чтобы сделать код еще прекраснее чем он есть (ведь твой код прекрасен, разве нет?).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Кроме этого, там есть такие штуки, как вызов редактора для исправления трассируемого файла, умный парсинг команд, команда &lt;code&gt;longlist (ll)&lt;/code&gt; для вывода на экран текущей функции или метода.&lt;/p&gt;
&lt;p&gt;Вот так выглядит типичная сессия pdbpp:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://bitbucket.org/antocuni/pdb/raw/0c86c93cee41/screenshot.png" /&gt;&lt;/p&gt;
&lt;p&gt;А для того, чтобы использовать этот чудесный инструмент, достаточно установить его в систему любым из доступных вам способов, после чего, как обычно, можно использовать &lt;code&gt;import pdb; pdb.set_trace()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; записал короткую демку того, как это работает.&lt;/p&gt;
&lt;p&gt;&lt;object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'&gt;&lt;param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' &gt;&lt;/param&gt;&lt;param name='flashvars' value='i=158211' &gt;&lt;/param&gt;&lt;param name='allowFullScreen' value='true' &gt;&lt;/param&gt;&lt;embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=158211' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer' &gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/wAy_cc0PG5Y" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/debugger-with-blackjack/</feedburner:origLink></entry><entry><title>Социальные закладки на GitHub</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/a3foh6KBvHA/" rel="alternate" /><updated>2011-01-13T11:17:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/gitmarks/</id><summary type="html">&lt;p&gt;Сегодня я хочу рассказать вам о довольно интересном проекте, появившемся недавно на GitHub.
Разрабатывается он некой девой Hilary Mason и называется &lt;a href="https://github.com/hmason/gitmarks/"&gt;gitmarks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Суть проекта в следующем. Он позволяет сохранять URL в закладки и делиться ими
с другими пользователями через GitHub. Для каждой закладки, скрипт выкачивает
содержимое залинкованной страницы. Так же, можно задать теги и описание
ссылки. Ну и конечно же, можно использовать &lt;code&gt;git grep&lt;/code&gt; для поиска по своим
закладкам. Добавлять ссылки можно как из командной строки, так и с помощью
букмарклета, но для этого нужно запустить локальный вебсервер.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://farm3.static.flickr.com/2262/2201268993_a560441f47.jpg" /&gt;&lt;br /&gt;
Автор фотографии &lt;a href="http://www.flickr.com/photos/eikumpel/2201268993/"&gt;Ei! Kumpel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Однако, у этого проекта есть, и несколько недостатков.&lt;/p&gt;
&lt;p&gt;Во-первых, код выглядит довольно безобразно из-за того, что Хилари не
придерживается общепринятых в Python сообществе, соглашений.&lt;/p&gt;
&lt;p&gt;Во-вторых, из той документации, что есть, совершенно непонятно, где следует
хранить закладки. В каком репозитории? Из-за этого, многие уже начали форкать
репозиторий, и добавлять закладки прямо в него, рядом с кодом, что захламляет
&lt;a href="https://github.com/hmason/gitmarks/network"&gt;network graph&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Стоило бы специальным образом отделить код проекта от контента, чтобы не
путать разработку с жизнью сервиса.&lt;/p&gt;
&lt;p&gt;Ну и в третьих, есть всякие мелочи, которые доставляют неудобства. Например,
предполагается, что люди могут комментировать закладки друг друга, прямо на
GitHub, оставляя коммент к коммиту. Однако сейчас страничка каждого коммита
выглядит довольно страшно, поскольку включает в себя весь HTML код по
добавленному URL. Этого можно было бы избежать, преобразуя HTML в читаемый
текст.&lt;/p&gt;
&lt;p&gt;В общем, надеюсь что проект получит развитие, и у нас появится новый,
распределенный сервис закладок.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/a3foh6KBvHA" height="1" width="1"/&gt;</summary><category term="python" /><category term="git" /><feedburner:origLink>http://dev.svetlyak.ru/gitmarks/</feedburner:origLink></entry><entry><title>Diff Git Cached в Vim</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/g_Vz5tdw0nI/" rel="alternate" /><updated>2011-01-09T14:04:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/diff-git-cached/</id><summary type="html">&lt;p&gt;Оказывается в Vim есть полезная штука для просмотре диффа при редактировании commit message git. Она появилась, начиная с версии 7.2. Называется эта команда :DiffGitCached.&lt;/p&gt;
&lt;p&gt;Дифф можно вызывать вручную, а можно прописать в конфиг такую строчку:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;autocmd&lt;/span&gt; &lt;span class="n"&gt;FileType&lt;/span&gt; &lt;span class="n"&gt;gitcommit&lt;/span&gt; &lt;span class="n"&gt;DiffGitCached&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;wincmd&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;И тогда дифф будет открываться автоматически после запуска &lt;code&gt;git commit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Этот хинт я почерпнул из книги &lt;a href="http://vim.runpaint.org/extending/integrating-vim-with-git/"&gt;Vim Recipes&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/g_Vz5tdw0nI" height="1" width="1"/&gt;</summary><category term="vim" /><category term="git" /><feedburner:origLink>http://dev.svetlyak.ru/diff-git-cached/</feedburner:origLink></entry><entry><title>Лучший логгинг для Питона</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/49JCy2TLKE0/" rel="alternate" /><updated>2010-12-30T10:39:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/better-python-logging/</id><summary type="html">&lt;p&gt;Помнится, я как-то уже жаловался на то, что код питонового логгинга писал какой-то ненатурал и индус, впридачу.
Так вот, недавно я нашел библиотечку, приятно реализующую логирования и по тестам, превосходящую стандартный
&lt;code&gt;logging&lt;/code&gt; в скорости, примерно в 5 раз. Называется она &lt;a href="http://packages.python.org/Logbook/"&gt;Logbook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Я потыкал в Logbook, поглядел код, позапускал тесты. Она мне нравится. В ней много вкусных плюшек. Вот те из
них, которые кажутся мне наиболее интересными:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Возможность работы со стандартной библиотекой &lt;code&gt;logging&lt;/code&gt;. Существующие библиотеки, которые выводят все
   через стандартный logging, переделывать не придется.&lt;/li&gt;
&lt;li&gt;Можно без проблем добавлять в записи дополнительную информацию, например IP пользователя и ID запроса
   к веб приложению. И тогда все записи лога будут легко кластеризоваться по этим данным.&lt;/li&gt;
&lt;li&gt;Специальные хендлеры, такие как &lt;a href="http://packages.python.org/Logbook/api/handlers.html#logbook.FingersCrossedHandler"&gt;FingersCrossedHandler&lt;/a&gt; и &lt;a href="http://packages.python.org/Logbook/api/handlers.html#logbook.GroupHandler"&gt;GroupHandler&lt;/a&gt; позволяют группировать записи лога
   и выводить их в файл (или отправлять по почте, пачками)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Особенно мне нравится идея, заложенная в обработчик &lt;a href="http://packages.python.org/Logbook/api/handlers.html#logbook.FingersCrossedHandler"&gt;FingersCrossedHandler&lt;/a&gt;. Суть заключается в том, что этот
обработчик "придерживает" все логируемые сообщения до того момента, когда будет встречено сообщение с заданным уровнем.&lt;/p&gt;
&lt;p&gt;Допустим, вы пишите сложную Django view в которой много чего происходит, внутри вызываемых вьюхой функций вы логируете
производимые действия с уровнем DEBUG или INFO. И когда всё идет как задумано, эти записи вам, в общем то, и не интересны.
А вот если случается какая-то ошибка, то было бы неплохо не только stack-trace посмотреть, но и видеть, какие действия ему
предшествовали. Тут то и приходит на помощь &lt;a href="http://packages.python.org/Logbook/api/handlers.html#logbook.FingersCrossedHandler"&gt;FingersCrossedHandler&lt;/a&gt;. Он складывает все записи лога в память, если
встречается запись с уровнем ERROR, то вызывает другой обработчик (например отсылающий записи из буфера вам на почту или
складывающий в файл). Если ошибки не произошло — буфер очищается, я специально проверил, утечек памяти нет. Вот
пример использования:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;with_statement&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;logbook&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FingersCrossedHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;issue_logging&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FileHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;app/issue-&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.log&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FingersCrossedHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;some_func&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;This is a debug message.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Warning, warning.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;issue_logging&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;some_func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Something went wrong.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;В итоге его работы, произойдет ошибка деления на 0 и в директории будет создан один единственный файл:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;DEBUG:&lt;/span&gt; &lt;span class="n"&gt;root:&lt;/span&gt; &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;WARNING:&lt;/span&gt; &lt;span class="n"&gt;root:&lt;/span&gt; &lt;span class="n"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;ERROR:&lt;/span&gt; &lt;span class="n"&gt;root:&lt;/span&gt; &lt;span class="n"&gt;Something&lt;/span&gt; &lt;span class="n"&gt;went&lt;/span&gt; &lt;span class="n"&gt;wrong&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="k"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;test.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
    &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;test.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;ZeroDivisionError:&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="n"&gt;division&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;modulo&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;zero&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;С помощью такого обработчика и возможности Logbook, расширять контекст дополнительными переменными, легко будет сделать
систему сбора информации об ошибки:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Нумеруем каждый запрос от пользователя и добавляем этот номер в контекст логгера.&lt;/li&gt;
&lt;li&gt;Оборачиваем обработку запроса в FingersCrossedHandler.&lt;/li&gt;
&lt;li&gt;В случае ошибки:&lt;ul&gt;
&lt;li&gt;Пишем отчет с полным логом в файл/базу, указывая номер запроса.&lt;/li&gt;
&lt;li&gt;Предлагаем пользователю отправить дополнительную информацию и рассказать о действиях, которые он производил перед
  тем, как возникла ошибка (разумеется, этот отчет будет связан с куском лога посредством его номера)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;По моему, очень круто. Постараюсь сделать подобное во всех, поддерживаемых мной проектах.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/49JCy2TLKE0" height="1" width="1"/&gt;</summary><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/better-python-logging/</feedburner:origLink></entry><entry><title>Image Shortener Wanted!</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/OCGAEBsokkQ/" rel="alternate" /><updated>2010-12-28T01:05:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/image-shortener-wanted/</id><summary type="html">&lt;p&gt;Меня посетила идея нового сервиса, от которого как минимум один человек, то есть я сам,
мог бы извлечь пользу — "Сокращалка Картинок".&lt;/p&gt;
&lt;p&gt;Порой, для иллюстрации поста в блоге, я нахожу в интернетах картинку, размещенную под
Creative Commons и просто вставляю ее в текст. Но ведь по хорошему, картинку нужно подгонять
под определенный размер, чтобы посты смотрелись хорошо.&lt;/p&gt;
&lt;p&gt;Так вот, было бы прекрасно, если б существовал некий прокси-сервер, который бы работал одновременно
и как сокращалка урлов, и как ресайзер.&lt;/p&gt;
&lt;p&gt;Например, есть картинка &lt;a href="http://farm5.static.flickr.com/4005/4203341399_c1c99027e5_o.jpg"&gt;http://farm5.static.flickr.com/4005/4203341399_c1c99027e5_o.jpg&lt;/a&gt;, и я хочу
вставить ее в блог, но так чтобы по ширине она была не больше 600 пикселей:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Я иду на сервис сокращалки и вбиваю там URL этой картинки (в идеале, хорошо бы чтоб можно было
   вбивать не только URL картинки, но например, URL странички фликра, где она размещена).&lt;/li&gt;
&lt;li&gt;Сервис выдает мне короткий урл: &lt;code&gt;http://blah.img/3bfg&lt;/code&gt; с которого, по умолчанию, просто редиректит
   на оригинал.&lt;/li&gt;
&lt;li&gt;Я вставляю в свой пост картинку, и в src указываю URL с параметрами: &lt;code&gt;http://blah.img/3bfg?width=600&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Когда по этому урлу картинка запрашивается впервые, сокращалка скачивает оригинал, ресайзит его
   кладет куда-нибудь в облако и редиректит на эту уменьшенную копию.&lt;/li&gt;
&lt;li&gt;Я получаю профит от красивого бложика.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ну как идея? Может я изобретаю велосипед, и она давно реализована?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/OCGAEBsokkQ" height="1" width="1"/&gt;</summary><category term="ideas" /><feedburner:origLink>http://dev.svetlyak.ru/image-shortener-wanted/</feedburner:origLink></entry><entry><title>Powered by Pelican</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/-r-TWneHEUc/" rel="alternate" /><updated>2010-12-25T23:06:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/powered-by-pelican/</id><summary type="html">&lt;p&gt;После нескольких дней трудов, перевел бложик на генератор статических блогов Pelican. При чем, даже не один бложик, а сразу два: &lt;a href="http://aartemenko.com"&gt;http://aartemenko.com&lt;/a&gt; и тот что я пытался вести в Google Buzz.&lt;/p&gt;
&lt;p&gt;От &lt;a href="http://aartemenko.com"&gt;http://aartemenko.com&lt;/a&gt; я решил отказаться с целью поэкономить память на VPS да и надоело столько доменов содержать. А Google Buzz оказался совсем непригоден для ведения коротких программистских заметок, поскольку непозволяет не то что код с подсветкой вставить, но даже простейших HTML тегов не поддерживает.&lt;/p&gt;
&lt;p&gt;В итоге, я взял довольно сырой движок для генерации статических блогов &lt;a href="https://github.com/svetlyak40wt/pelican"&gt;Pelican&lt;/a&gt;, и немного его доработал. Доработка заключалась в исправлении нескольких багов и добавлении возможности перевода статей на несколько языков (я хочу попробовать переводить некоторые заметки на английский). Кроме того, я портратил примерно полтора дня на разработку своей темы для Pelican.&lt;/p&gt;
&lt;p&gt;Теперь я могу редактировать посты в своем любимом текстовом редакторе Vim, и при необходимости, запускать по ним &lt;code&gt;grep&lt;/code&gt;. Пока Pelican поддерживает снитаксисы Markdown и ReST, но думаю этого должно хватить всякому. Осталось дописать вспомогательный скрипт для Vim, который будет деплоить статьи на хостинг, и пингать FeedBurner.&lt;/p&gt;
&lt;p&gt;Кстати, а вот и &lt;a href="https://github.com/svetlyak40wt/dev.svetlyak"&gt;исходники этого блога&lt;/a&gt;, я решил выложить их на GitHub в качестве примера использования модифицированного Пеликана.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/-r-TWneHEUc" height="1" width="1"/&gt;</summary><category term="life" /><category term="python" /><feedburner:origLink>http://dev.svetlyak.ru/powered-by-pelican/</feedburner:origLink></entry><entry><title>Модульная сетка</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/oMFngRi5yNo/" rel="alternate" /><updated>2010-12-19T20:52:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/modular-grid/</id><summary type="html">&lt;p&gt;Настраиваю модульную сетку для своего бложика.&lt;/p&gt;
&lt;p&gt;Решил отказаться от Buzz и переехать в статический stand-alone,
генерируемый с помощью &lt;a href="https://github.com/svetlyak40wt/pelican"&gt;Pelican&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Сейчас делаю свою тему. CSS буду генерить с помощью Compass и Blueprint, а для
вспоможения, настроил отображение модульной сетки с помощью вот этого
джаваскрипта:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/aishek/modulargrid"&gt;https://github.com/aishek/modulargrid&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/oMFngRi5yNo" height="1" width="1"/&gt;</summary><category term="html" /><category term="layout" /><feedburner:origLink>http://dev.svetlyak.ru/modular-grid/</feedburner:origLink></entry><entry><title>Pdb decorator</title><link href="http://feedproxy.google.com/~r/dev-svetlyak/all-ru/~3/0Ztd3hFPrjM/" rel="alternate" /><updated>2010-12-16T14:43:00Z</updated><author><name>Александр Артеменко</name></author><id>http://dev.svetlyak.ru/pdb-decorator/</id><summary type="html">&lt;p&gt;Нарисовал тут pdb decorator. Он полезен, когда отлаживаешь функцию, обернутую
большим кол-вом декораторов, например какую-нибудь Django вьюху.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wraps&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pdb_deco&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;pdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_trace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/dev-svetlyak/all-ru/~4/0Ztd3hFPrjM" height="1" width="1"/&gt;</summary><category term="python" /><category term="snippet" /><feedburner:origLink>http://dev.svetlyak.ru/pdb-decorator/</feedburner:origLink></entry></feed>

