Deepwalker
пятница, 14 декабря 2012 г.
Ruby vs Python
вторник, 6 марта 2012 г.
Мэтрам django web разработки посвящается
Я так считаю - любую задачу можно решить на тьюринг полном языке, вопрос в удобстве. Вот одно время программисты писали кругом goto и gosub, а потом пришла к ним мысль, что это не совсем удобно.
Но долгое время в сторону паскаля неслись смешки, и говорили - нафиг нам ваши break и continue, да я с goto решу выход из цикла на раз, в три раза элегантнее.
А потом как-то обнаружилось, что без goto все таки лучше, а главное нагляднее - не обязательно теперь программу в блок-схемах рисовать, чтобы прикинуть редьку к луку.
Или вот можно писать::
if key in map:
return map[k]
else:
return None
А можно элегантнее действовать, можно написать `return map.get(k)`. А говорят еще есть некий монадический Option путь, там вообще трава зеленее. Но `if ... in ...` он проще и понятнее, я if уже двадцать лет знаю и умею, идите нафиг, я еще в великой энциклопедии самого профессора Фортрана читал. Я может потом, как время будет, прочту про хаскель сразу, и не буду писать ни одного `if` при разборе json. Но это конечно как время будет, а то я же человек серьезный, я сайты пишу - много if-ов, некогда мне, некогда.
Или вот про фреймворки все же - вот джанга она столп, а может столб, тут мнения разделились, что в этих ваших интернетах вообще не редкость.
Говорят под нее много приложений, хороших, бери пользуйся. Все написаны метрами, типа меня. Православный goto, if и никакой монадической зауми.
Или вот шаблоны - сказали логики в шаблонах ни-ни, ну вот и ни-ни - логику отображения быстренько размазали по templatetags и тихо там у меня, революционеры гадкие, поди еще и на болотную ходили. И макросы в шаблонах не нужны, и вызов функций с параметрами - от лукавого все это. А что тормозит, то все от свершений.
ORM слабый? Да пишите SQL уже, как все нормальные мужики. И формы наши эталон, CSRF у них талон, идите нафиг.
И вообще, у меня все проекты на бейсике, я его хорошо знаю, еще со школы и зеленоэкранных ямашек.
Но шутки в сторону. Скажите мне замшелые троглодиты из реально нереально серьезной веб разработки, что, экономия на рендере шаблонов это лишнее для проектов с посещаемостью >1000? Более быстрая разработка с более гибким ORM это лишнее? Да вы вообще БД используете, или планируете проект на sqlite запускать в продакшн? Даже обработка URL в Werkzeug быстрее. Можно говорить что где-то это копейки, но когда все копейки собираются во Flask, это уже становится серьезно, мимо проходить уже как-то непрофессионально,
и даже как-то стыдно.
За пределами уютного мирка в злато-зеленых тонах кипит жизнь, возникают новые идеи и инструменты, но всегда есть мэтры, которым некогда улучшать свои навыки - у них 1000 пользователей ждет, когда мэтр напишет все if-ы.
среда, 18 января 2012 г.
SamaraPy
Подробности будут тут http://vk.com/club34256705
В конце концов оказался же в этом городе Я!
FreeSWITCh, love over
Я больше не занимаюсь FreeSWITCH по нескольким причинам. Первая - я теперь не сетевой администратор с горкой АТС, и не работаю в VoIP стартапе.
Вторая - считаю разработчиков FreeSWITCH-а неадекватными по нескольким пунктам.
1. Прием патчей. Когда у нас глюкало видео я искал проблему и нашел ее. Создал патч и отправил в жиру. После месяца боданий патч был принят с формулировкой "а предыдущий пацанчик говорил что все работает отлично, но вы достали, хрен с вами". Многообщающе.
2. Использование DMCA для блокировки репозитория с кодом g729 кодека. Нормальные люди вначале пишут и говорят - у вас тут мои копирайты, вы поправьте. В общем мне их мотивировка неясна - их права никак не были нарушены, но шило в попе творит чудеса.
В общем как для профессионального разработчика для меня FreeSWITCH это один сплошной дурдом. Без причин с ним возиться я с ним возиться никакого желания не имею.
вторник, 23 февраля 2010 г.
Django в неблокирующем стиле, или в погоне за Священным Граалем

Присказка
При чтении о Twisted, Tornado, Node.js, у многих python-программистов возникает вопрос - "а вот если взять, и переписать Django в неблокирующем стиле?". Обычный ответ на этот вопрос - нет, не дождетесь. И правда, чтобы переписать целый фреймворк в макаронно-колбечном стиле, надо очень много сил, и большой заряд энтузиазма. Писать с колбеками, очень сомнительное удовольствие.Так бы и было, но как я писал в своей прошлой заметке, есть в python-мире greenlet-ы, которые легким движением руки помогают скрыть от программиста все эти асинхронные моменты, до определенной степени. В момент написания той заметки, к мысли заставить работать Django в неблокирующем режиме, я относился весьма скептически - ну в самом деле, кто за это возьмется?
Суть
А на самом деле все не так страшно, следите за руками - мы создаем WSGI ресурс в Twisted, начинаем слушать HTTP-запросы; каждый запрос мы отдаем в WSGI-обработчик Django, оборачивая его в гринлет; любые запросы, которые можно преобразовать в неблокирующие, передаем Twisted, который пнет наш гринлет в момент готовности данных; готово!
А что же у нас есть такого, что можно обратить в неблокирующий режим в первую очередь? В Django основным таким местом выступает база данных. Можно ли с ней общаться асинхронно? Абсолютно верно, но нужен специальный драйвер.
С давних пор в Twisted есть такой - PGAsync. У него есть серьезные проблемы, он давно заброшен, но кто нам мешает подхватить упавшее знамя? Я сделал такую попытку, на данный момент драйвер работает стабильнее оригинала, но работа еще не закончена. Тем не менее, асинхронную БД для Django мы получить можем, и это будет Postgresql (ох, простите, пользователи Mysql, но кто вам мешает написать свой?). Драйвер используется в текущем проекте, разрабатываемом Imarto Networks http://imarto.net, и было решено выложить его для свободного использования. Я выложил его раньше готовности, только для демонстрации асинхронного Django, будьте снисходительны.
Чтобы использовать его в Django, нам надо написать свой БД-бэкенд. Я наворотил что-то из бэкенда для psycopg2, обернув обращения к драйверу БД в функции передачи управления главному гринлету, то есть основному потоку программы. Стандартный wsgi-ресурс из twisted.web пришлось тоже немного переделать, ведь он теперь должен кидать запросы на обработку не в пул потоков, а в гринлеты.
Как пощупать
Исходники можно получить тут, там же тестовый django-проект - http://bitbucket.org/deepwalker/tx_green/Исходники драйвера к БД - http://bitbucket.org/deepwalker/tx-postgresql/
Для использования надо разместить pgasync, green_wsgi.py, tx_green.py, tx_postgresql в место, где интерпретатор сможет их найти. Затем в каталоге test_green_dj настраиваем параметры подключения к БД, ./manage.py syncdb, twistd -n - y server.py. Все, по адресу http://127.0.0.1:8001 встречаем наш тестовый проект.
Про тесты
Тесты, которые я проводил, относились в основном к проверке работы, прироста производительности я не измерял. Если кто-то потратит свое время на это доброе дело, буду благодарен, если поделитесь результатами.суббота, 9 января 2010 г.
Озеленение Twisted
Помучавшись некоторое время, я переписал defer.inlineCallbacks на использование greenlet-ов и смог написать такой код:
from twisted.internet import defer, protocol, reactor
from tx_green import inlineCallbacks
from greentokyo import Tyrant
@inlineCallbacks
def test_proto():
t = Tyrant()
print t.get_stats()
t['kuku'] = 'Green Tyrant!'
print t['kuku']
reactor.stop()
if __name__=='__main__':
test_proto()
reactor.run()
Удобство в том, что greenlet-ы имеют важное отличие от генераторов - они не ограничены одной функцией. То есть при вызове t['kuku'], на самом деле вызывается t.__getitem__, внутри которого мы и делаем вызов switch() для возврата управления основному циклу на время, пока мы ожидаем данные от сервера. Если бы мы попробовали сделать внутри t.__getitem__ yield, то мы бы просто сделали из него генератор, не получив никакой пользы.
Неудобство состоит в том, что надо помнить обо всей этой начинке - будет плохо если вы будете ждать данные, а вместо этого вам упадет просто Deferred. И будет еще хуже, если вы забудете вернуть управление основному потоку - остановите все приложение. И еще в этом варианте не светит "not twist your brain", потому что надо помнить и использовать возможности Twisted.
Стоит обратить внимание, что в приведенном коде нет ничего, явно указывающего на возврат управления - вызовы greenlet.switch() скрываются в реализации питоничного Tyrant. То есть он заточен под эту реализацию. Если бы это было не так, то нам бы пришлось явно вызывать функцию wait из tx_green. Именно этот момент меня беспокоит - можно создать чуть ли не джангу, и обращения к БД через ORM будут работать также скрытно вызывая wait, но если забыться, то потенциально можно породить трудноуловимую ошибку, или новичок, как обычно не прочитав толком документацию, начнет запрашивать с какого-либо сервера странички через urllib и будет долго удивляться, почему сервер подтормаживает.
Реализация расположена в моей песочнице.
Вот такой небольшой пост. Как всегда ожидаю приятного обсуждения, и надеюсь, что будут хорошие предложения по коду, как было в прошлой нашей встрече, со мной в амплуа постописателя. Спасибо за внимание.
воскресенье, 13 декабря 2009 г.
Twisted в действии — memcache на python
Преамбула
В связи с выходными потратил немного времени на реализацию сервера Memcache с использованием python-фреймворка Twisted. В итоге я получил быстродействие в два раза более низкое, что я не считаю очень критичным, а также возможность реализовать парочку расширений оригинального протокола. Также возможны оптимизации, которые еще улучшат быстродействие.Протокол не был реализован полностью - есть еще моменты над которыми можно поработать, но стандартные set/get вполне работоспособны и готовы к использованию.
Средства
Для хранения кеша используем базовый класс dict. Как вы догадываетесь, реализация dict в python быстра, этот базовый тип используется в python настолько активно, что его не оставили без детальной оптимизации. Таким образом, мы автоматом имеем структуру для хранения кеша в памяти. Осталось реализовать протокол memcache, для предоставления доступа к dict другим программам.Для реализации сервера используем Twisted. Есть множество вариаций неблокирующего IO для python на сегодня, но Twisted это уже классика, и имеет в своем арсенале достаточно средств для легкого решения подобных задач.
Реализация сетевого протокола
Как реализуют протоколы? Первым делом вам конечно же нужно найти описание протокола. Я нашел его здесь - http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txtПосле прочтения протокола становится понятно, что от клиента мы получим одну или две строки, причем первую строку мы можем смело разбивать на элементы по пробелам. Вторая строка используется в командах, которые передают серверу данные - set, add, replace и т.п. Если вам хочется подробнее вникнуть в статью, то отправлю вас почитать описание самостоятельно, цели выложить его перевод сюда не было.
Вооруженные этим знанием, смотрим, что нам может предложить Twisted для решения этой задачи, и сразу находим LineOnlyReceiver - протокол из базовой поставки Twisted, который работает только с протоколами, обменивающимися строками, то есть то, что надо.
class MemcacheProtocol(LineOnlyReceiver):
"""
Реализует базис протокола - прием сообщений от клиента
и отдачу результата.
"""
def lineReceived(self,line):
debug(repr(line))
if not 'parameters' in self.instruction:
parameters = line.split(' ')
debug("Got new command "+parameters[0])
self.instruction['parameters']=parameters
# Если данных не ожидается, то к исполнению
if parameters[0] in Cache.oneline_commands:
self.process()
else:
# Получены данные к двухстрочной команде, к исполнению
debug("Got data "+line)
self.instruction['data']=line
self.process()
def process(self):
# Cache.call возвращает генератор
for line in Cache.call(self.instruction):
# И мы отсылаем все что он нагенерирует отдельными строками
debug("Send line "+line)
self.sendLine(line)
# Готовы к дальнейшим инструкциям, насяльника!
self.instruction={}
def connectionMade(self):
debug("Connected!")
self.instruction={}
Как видно из кода, для собственно работы используется Cache. Это синглетон, по сути просто класс, методы которого обернуты декоратором @classmethod. Вызов Cache.call должен вернуть генератор, которые будет возвращать строки, которые, в свою очередь, наша реализация протокола, будет отдавать клиенту.
Разбираем запрос от клиента
Первая строка это команда и параметры, разделенные пробелами, поэтому используем строковый метод split, и на выходе получаем список. Далее его надо разобрать на составляющие, перед тем как с данными начнет работать команда. Я использую класс, так как мне нравится перспектива обращаться к параметрам, указывая их через точку. Приведенный ниже код уже требует прочтения описания протокола, а для ленивых пара наводящих строк:Команды записи данных:Реализация разбора:[noreply]\r\n cas [noreply]\r\n Получение данных: get *\r\n gets *\r\n delete \r\n Ну и тому подобное.
class Instruction(object):
def __init__(self, i):
p = i['parameters']
self.cmd = p.pop(0)
# Проверяем noreply
if p[-1]=='noreply':
self.reply=False
# Выкидываем его
p.pop(-1)
else:
self.reply=True
if self.cmd in Cache.storage_commands:
# Если CAS то есть еще один параметр (т.е. особый случай)
if self.cmd == "cas":
self.unique = p.pop(-1)
# Теперь все параметры однозначны, но мы хотим расширить протокол,
# потому все не так просто, как dict(zip())
self.bytes = p.pop(-1)
self.exptime = p.pop(-1)
self.flags = p.pop(-1)
self.data = i.get('data',None)
# incr, decr
elif self.cmd in ["incr","decr"]:
self.change_value = p.pop(-1)
self.keys = p
def __str__(self):
return str(self.__dict__)
Реализация хранения кеша и работы с ним
Протокол мною сразу же был расширен, а именно есть возможность работы с вложенными данными. Кеш переделан в древовидный, и все операции, которые по стандарту указывают один ключ, могут указывать список ключей, разделенных пробелами. Впрочем от этого легко избавиться, но тогда будет совсем неясен смысл работы.В качестве единицы хранения реализован класс Entry, в котором содержится словарь(childs типа dict) с дочерними экземплярами Entry. Более того - верхней точкой в иерархии также является экземпляр класса Entry.
Здесь же я приведу фрагмент синглетона Cache:
class Cache(object):
# consts
storage_commands = ["set", "add", "replace", "append", "prepend","cas"]
oneline_commands = ["get", "gets","getn", "delete", "incr", "decr", "stats"]
# cache storage
data = Entry(0,0,0)
# cache operations
@classmethod
def call(cls, instruction):
i = Instruction(instruction)
debug(i)
command = getattr(cls,i.cmd)
return command(i)
@classmethod
def set(cls, i):
"set, поддержка вложенных ключей"
parent = cls.data.get_child(i.keys[:-1])
if parent:
parent.set_child(i.keys[-1], Entry(i.data,i.flags,i.exptime))
yield "STORED"
else:
yield "NOT_STORED"
@classmethod
def get(cls, i):
"get, не обрабатывает вложенные ключи"
for key in i.keys:
entry = cls.data.get_child([key])
if entry:
yield ' '.join(( "VALUE", key, entry.flags, str(len(entry.data)) ))
yield entry.data
yield "END"
Код Entry и всего остального смотрим тут - http://github.com/Deepwalker/tx-cache/blob/master/mck.py