Python: Веб-разработка без фреймворков (часть 1)

Каждый разработчик тщательно выбирает свой инструментарий и чем лучше он им владеет, тем эффективнее его работа и тем более востребованы его услуги. В то же время многие программисты гордятся своей ленью и ищут такие инструменты, которые делали бы за них как можно больше работы. При этом, на мой взгляд, забывают, что бесплатный сыр только в мышеловке и гоняются за иллюзорным «лучшим» фреймворком тщательно сверяя какой из них делает больше работы: Zope, ROR или Django? Мой опыт говорит о том, что время на изучение фреймворков и подстройка под их ограничения почти никогда не окупается, а пользуясь минимальным инструментарием, с которым я хочу вас ознакомить, можно добиться гораздо лучших результатов.

WSGI

Не так давно библиотек и фреймворков для веб-разработки на Python было очень много, и взаимодействовали они между собой не наилучшим образом. Выбрав для разработки одно решение приходилось придерживаться его и в дальнейшем. Разрешением этой проблемы многие считали выбрать один-два главных фреймворка и сконцентрироваться на их разработке, таким образом сделав остальные ненужными. Такая консолидация решила бы проблемы с взаимодействием разных частей, но и минусов у неё порядочно. Ведь так много библиотек возникло не зря, и причина этого именно в том что нет очевидно предпочтительного подхода к веб-разработке.

Решение подоспело в виде стандарта WSGI (PEP-333). Это сверх-компактная спецификация, которая решает как вопросы взаимодействия разных компонент веб-приложений, так и связки между этими приложениями и HTTP-сервером.

Вкратце, WSGI (Web Server Gateway Interface) требует от приложения предоставлять следующий интерфейс:

def app(environ, start_response):
    """ 
    (dict, callable( status: str, 
                     headers: list[(header_name: str, header_value: str)]))
                  -> body: str or iterable of strings
    """

Т.е. приложение будет вызвано с двумя аргументами. Первый — словарь environ, содержащий различные данные о запросе, второй — функция запускающая процесс ответа на запрос. В основном содержимое environ совпадает с содержимым переменных окружения CGI скрипта при аналогичном запросе, откуда и происходит имя этого аргументa, это же позволяет использовать уже имеющиеся средства для разбора строки запроса итп. Назначение start_response проще показать на примере, а возвращать WSGI приложение должно либо строку, либо итератор по строкам, т.е. функция может быть генератором и «скармливать» веб-серверу ответ по частям. Например:

import cgi
def hello_app(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    yield "Hello, "
    form = cgi.FieldStorage(environ=environ)
    name = form.getfirst('name', 'stranger')
    yield name

Удобство использования WSGI заключается, конечно, не в том, что мы можем писать такой код, а в том, что предоставив такой интерфейс, мы получаем доступ к множеству совместимых библиотек. Например, мы элементарно можем запустить сервер с вышеуказанным приложением:

from paste.httpserver import serve
serve(hello_app)

Мы можем также предоставить его через FastCGI, SCGI или еще какой-нибудь диковинный протокол общения между HTTP-сервером и сервером приложений. Существует также mod_wsgi, модуль Apache, позволяющий в считанные минуты запустить ваше приложение через этот сервер, причем с отличной интеграцией с его инфраструктурой.

Ну и, конечно же, мы можем вызывать другие WSGI приложения и обрабатывать их результаты (о чем будет сказано в разговоре о middleware). Этот стандарт к тому же делает возможным библиотеки тестирования веб-приложений, не привязанные ни к каким фреймворкам. В результате библиотек для веб-разработки врядли стало меньше, ведь создавать новые теперь куда проще, но проблема совместимости была решена. Теперь любой фреймворк, вне зависимости от того каким образом он сообщается с вашим кодом, предоставляет WSGI. Различные приложения, не использующие сторонних фреймворков, также доступны через WSGI (например Trac).

PythonPaste

PythonPaste (paste — тесто) это замечательное собрание различной функциональности реализованной для WSGI и общей для разных фреймворков. Если вы заглянете своей любимой библиотеке под капот и не найдете там Paste, то выбор ваш возможно не так уж хорош. Впрочем, сам Paste настолько удобен, что ознакомившись с его возможностями, не должно возникнуть желания использовать какой-либо фреймворк вообще.

Например, в нем есть готовая поддержка самых разных способов аутентификации (Basic, Digest, form, signed cookie, auth_tkt), поддержка корректной и удобной генерации ответов и заголовков (к примеру редиректы, Cache-control, Expires, gzipper и прочие). Различные базовые средства комбинации приложений (URLMap, Cascade, Recursive), статических данных (с учетом Etag, If-Modified итп). Скажем так:

from paste.urlmap import URLMap
from paste.fileapp import FileApp

root_app = URLMap()
root_app['/static'] = FileApp('/var/www/static/')

from paste.auth.digest import digest_password, AuthDigestHandler

def private_app(environ, start_response):
    remote_user = environ['REMOTE_USER']
    app = FileApp('/home/%s/htdocs/' % remote_user)
    return app(environ, start_response)

def authfunc(environ, realm, username):
    return digest_password(realm, username, username) # password == username

private_wrapped = AuthDigestHandler(private_app, "Private area", authfunc)
root_app['/private'] = private_wrapped

Как видите, кода написано минимум, а часть сайта уже защищена паролем и соответствует разным папкам на диске в зависимости от имени пользователя. Представить себе, что нечто подобное можно записать короче и понятней, трудно.
Можно также предоставлять статические файлы прямо из архива (ArchiveStore), есть поддержка сессий, возможность отслеживать прогресс загрузки на сервер больших форм, и прочее и прочее.

PythonPaste для отладки

Особо радуют средства для отладки приложений. Тут и возможность приложения автоматически перезапускаться при изменении части исходных кодов, и профайлер для каждого запроса, и возможность слать отчеты об ошибках на почту (или сохранять их локально). Можно валидировать все [X]HTML ответы сервера, есть те самые средства тестирования WSGI приложений итд итп. Есть даже возможность интерактивной отладки прямо в браузере. На этом стоило бы остановиться особо, но это лучше один раз увидеть, чем сто раз прочитать, просто запустите этот код и откройте в браузере localhost:8080. (Также можно посмотреть скринкаст).

from paste.evalexception.middleware import EvalException
def error_app(environ, start_response):
    raise ValueError

from paste.httpserver import serve
serve(EvalException(error_app))

Далее

В следующих частях я собираюсь рассказать про использование WebOb, написание middleware, возможности mod_wsgi и вопросы конфигурации WSGI приложений.

Все про українське ІТ в телеграмі — підписуйтеся на канал DOU

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn



23 коментарі

Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.

Более чем через год возращаюсь к статье, хочу выразить свою признательность как Сергею Щетинину, так и разработчику WebOb — Ian Bicking.По поводу того, что в своих статьях Сергей настаивает, что фреймворк не нужен, то мне так не кажется. Даже думаю больше — те практики, которые используются чаще других просто призваны создать ядро, каркас или основу — как угодно будет назвать, очередному фреймворку. Ведь с одной стороны есть необходимость создавать типовые решения, а с другой — стандарты достаточно жестко очерчивают границы возможноного, поэтому им дано было появиться.И на текущий день фреймворки получившие наибольшую популярность также создают подобие стандарта — де-факто. Они могут быть не самыми понятными, и тем более — не самыми безопасными и быстрыми. Они просто создавались имея основу в виде примера реализации на других языках программирования.Вот и у меня получилось что-то вроде фремворка:)

@bitl, я поправлю только в одном. Почти каждый раз, возражая мне на эту тему говорят о том, что «может лучше быстрее с фреймворком», хотя я в статьях много раз повторил что без фреймворков я пишу в несколько раз быстрее. То есть в 2−3 раза быстрее.Статьи вообще не о том что «писать с нуля хорошо», а о том как правильно писать без фреймворков. Повторяю по слогам: как именно ПРАВИЛЬНО писать БЕЗ ФРЕЙМВОРКОВ. Не с нуля, а без фреймворков. И не лишь бы как, а как правильно.Если при этом кто-то думает про себя что «я не понимаю фреймворков, я сделаю свой лучше», то я тут ни при чем. Если я и говорю о простоте решений в лоб то это потому что они простые и есть. Но при этом бытует мнение что фреймворк необходим (свой или готовый). Да я и сам так считал. Но вот незадача, оказалось для того чтобы отделить обработку данных и их отображение фреймворк не нужен итп.

Некоторые мои мысли относятся к статье, некоторые к другим комментариям.И пусть никто ничего не воспримет негативно.Так-то оно так, но если модель данных достаточно сложна и динамических страниц со сложным разнообразным выводом надо несколько десятков, то писать такое, а потом сопровождать без нормального framework-а та еще жесть.MVC концепцию тоже не дураки придумывали.И отделение обработки данных от их отображения посредством шаблонов — совсем даже не зло.Ну, а если web-программист ещё не может разобраться в каком-нибудь web-framework, ему есть чему поучиться и не стоит воспринимать весь этот материал как панацею.Задачи в жизни разные встречаются.Благо туторов к хорошим фреймворкам есть. Тутор к django позволяет освоиться за несколько десятков минут. И вовсе он не ограничивает творчество: -) Сам программировал серьёзные проекты, как с помощью различных фреймворков (struts (java), django (python), smarty (php)), так и без них.Например, когда надо сделать некий шлюз между разными протоколами, тогда фреймворк точно не нужен, достаточно одного сервлета или cgi-скрипта.А в реальной жизни web-программиста нередко web-фреймворк обязателен.И может, все таки, лучше изучить сначала пару фреймворков, а потом быстрее писать на них десятки проектов, чем каждый проект писать с нуля.Ато вырастет поколение программистов, каждый раз изучающих чьи-то снулевые пректы заново, при надобности сопровождения, вместо того чтобы разрабатывать нечто, написанное на знакомом фреймворке.

Посмотрел оглавление, там вообще другое, всякие нормы и шаблонные решения. У меня фристайл!)

Поддерживаю BigHo, я бы тоже мог купить такую книгу.

BigHo, ухты, спасибо. =) Не стесняйтесь такие каменты писать, я издателю покажу. =)

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

спасибо, очень познавательно и оптимистично

На всякий случай повторю, что не знаю насколько хорошо сработает такой подход для новичков / чайников и поэтому стараюсь давать материал таким образом чтобы нужно было часть шагов выполнять самому, если с ними возникают проблемы, то возможно не стоит слепо копировать всё остальное. Вообще для новичков я пожалуй порекомендовал бы CherryPy, там достаточно быстро можно приступить к работе.Приведенный тут код как раз можно просто запускать (см. упомянутый paste.httpserver). Ну и прочитайте остальные статьи в серии, в частности http://www.developers.org.ua/a.../

Спасибо, статья добавила оптимизма. Который день бъюсь над Hellow world в Django и ничего не получается. Т.е. не могу настроить всю связку. Мне проще с нуля разрабатывать, чем настраивать интеграцию всего этого кампота c фрейворками. Пожалуйста приведите пример или подскажите где почитать пошаговую, понятную для чайников инструкцию, настройки Apache+WSGI+Python+IDE (например Eclipse/PyDev) для разработки чисто под WSGI.Как выполнить код приведенный в статье? Ибо «просто запустить» код я не могу.

Александр, спасибо, гляну подробней. Хотя, страшно сказать, но я на данный момент от ORM отказался совсем. Для веб-приложений с какого-то момента так вообще декларации для таблиц не пишу — сразу в базе их создаю, а в питоне

Table("...", dbmeta, autoload=True)

. Работает всё, надо сказать, ну ничуть не хуже.

Сергей, в SQL Alchemy помимо Elixir есть еще плагин Declarative (http://www.sqlalchemy.org/docs...). С ним работа, конечно, не настолько простая и гибкая как с Elixir, зато вся гибкость SQL Alchemy остается.

bialix, спасибо, про сайт знаю — хадвер подвел.

Спасибо, познавательно. Даже для такого полного чурбана чайника в веб-разработке как я.ЗЫ: Сергей, у тебя сайт лежит.

Пошел читать про изменения в Пилонах и попалась статья про SQLAlchemy / Elixir и про то что по большому счету последний — зло. Я об этом тоже собираюсь кое-что написать, и даже пожалуй радикальнее, но пока что рекомендую ознакомиться с тем что есть.

Не знал что уже используют. =) Молодцы значит.

... расскажу про WebOb, а по сравнению с ним у фреймворков уже очень мало преимуществ

Спасибо, обязательно почитаю. Сам пока не добрался:) Очень интересно, так как Пилоны, начиная с 0.9.7, используют request/response objects from WebOb.

Я думаю что популярность фреймворков связана с тем, что неопытный программист сначала ищет чего бы почитать на тему веб-разработки (и это правильно), а статьи и книги в основном о том, как писать при помощью разных навороченных фреймворков. Несколько упрощая ситуацию можно сказать что по Zope можно написать сотню книг, а по Paste едва ли одну. Zope едва ли в 100 раз полезней. То же касается Java vs. Python, REST vs. SOAP / WS-* итд.

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

Совсем чистый WSGI и вправду не всегда самый удобный вариант, но вот в следующей статье я расскажу про WebOb, а по сравнению с ним у фреймворков уже очень мало преимуществ. Я как раз за такой подход как у вас — составлять свой мини-фреймворк, или скорей все таки, библиотеку для веб-разработки. Pylons собираюсь тоже чуток поругать.

Сергей спасибо за интересную статью.Согласен отчасти по поводу окупаемости, но мне кажется что Pylons здесь исключение, так как он не навязывает, а рекомендует. RoR тоже неплох, а вот Django слишком ограничивает творчество, что не свойственно для Python фреймворков.Может для зубров которые «на ты» с WSGI Pylons и не нужен, но мы с нуля сколотили на нем свой собственный мини-фреймворк и очень довольны. И результатом и скоростью разработки. Хотя конечно многие вещи мы выкинули или переточили, но начинать совсем с WSGI было бы сложнее и наверное мы бы не писали на Python совсем

Підписатись на коментарі