Кеннет Рейтц – Автостопом по Python (страница 33)
2. Это приложение вернет итерабельный объект, содержащий объекты типа bytestrings, который сервер использует для ответа на запрос HTTP.
3. В спецификации также говорится, что приложение примет два параметра, например webapp(environ, start_response). Параметр environ будет содержать данные, связанные с запросом, а параметр start_response будет функцией или другим вызываемым объектом, который задействуется для отправки обратно заголовка (например, ('Content-type', 'text/plain')) и состояния (например, 200 OK) на сервер.
В этом документе есть еще полдюжины страниц дополнительной информации. В середине PEP 333 вы можете увидеть впечатляющее заявление о том, как новый стандарт сделал возможным создание веб-фреймворков.
Если промежуточное ПО может быть простым и надежным и стандарт WSGI широко доступен в серверах и фреймворках, появляется возможность создания принципиально нового фреймворка Python для веб-приложений, — фреймворка, который состоит из слабо связанных компонентов промежуточного ПО WSGI. Более того, авторы существующих фреймворков могут решить изменить свои сервисы, сделав их похожими на библиотеки, используемые вместе с WSGI, а не на монолитные фреймворки. Это позволит разработчикам приложения выбирать лучшие компоненты для требуемой функциональности, а не довольствоваться одним фреймворком, воспользовавшись всеми его преимуществами и смирившись с недостатками.
Конечно, на текущий момент этот день еще не так близок. В то же время хорошей кратковременной целью для WSGI является возможность использовать любой фреймворк с любым сервером.
Четыре года спустя, в 2007 году, Армин Ронакер (Armin Ronacher) выпустил Werkzeug, намереваясь создать библиотеку WSGI, которую можно использовать для создания приложений WSGI и компонентов промежуточного ПО.
Werkzeug — самый крупный пакет из тех, что мы рассмотрим, поэтому перечислим только несколько его проектных решений.
Читаем код инструментария
Инструментарий (тулкит) — это набор совместимых утилит. В случае Werkzeug все они связаны с приложениями WSGI. Хороший способ понять отдельные утилиты и их предназначение — взглянуть на юнит-тесты, именно так мы и поступим.
Получаем Werkzeug из GitHub:
$ git clone https://github.com/pallets/werkzeug.git
$ virtualenv — p python3 venv
$ source venv/bin/activate
(venv)$ cd werkzeug
(venv)$ pip install — editable.
(venv)$ py.test tests # Запускаем юнит-тесты
В документации Werkzeug (http://werkzeug.pocoo.org/) перечислены основные возможности, предоставляемые библиотекой, — реализация спецификации WSGI 1.0 (PEP 333, https://www.python.org/dev/peps/pep-0333/), система маршрутизации URL, возможность анализировать и сохранять заголовки HTTP, объекты, которые предоставляют запросы и ответы HTTP, сессии и поддержка cookie, загрузка файлов и другие утилиты и надстройки от сообщества. Плюс полноценный отладчик.
Эти руководства информативны, но мы используем документацию к API вместо того, чтобы изучать компоненты библиотеки. В следующем разделе рассматриваются обертки для Werkzeug (http://werkzeug.pocoo.org/docs/0.11/wrappers/) и документация по маршрутизации (http://werkzeug.pocoo.org/docs/0.11/routing/).
Werkzeug предоставляет вспомогательные программы для приложений WSGI, поэтому, чтобы узнать, что предоставляет Werkzeug, мы можем начать с приложения WSGI, а затем использовать несколько вспомогательных программ от Werkzeug. Это первое приложение несколько отличается от того, что предоставлено в PEP 333, и не использует Werkzeug. Второе приложение делает то же самое, что и первое, но при этом использует Werkzeug:
def wsgi_app(environ, start_response):
····headers = [('Content-type', 'text/plain'), ('charset', 'utf-8')]
····start_response('200 OK', headers)
····yield 'Hello world.'
# Это приложение делает то же самое, что и указанное выше:
response_app = werkzeug.Response('Hello world!')
Werkzeug реализует класс werkzeug.Client, который ведет себя как реальный веб-сервер при выполнении подобных проверок. Ответ клиента будет иметь тип аргумента response_wrapper. В этом коде мы создаем клиентов и используем их для вызова приложений WSGI, которые создали ранее. Для начала разберем простое приложение WSGI (его ответ будет размещен в объекте werkzeug.Response):
>>> import werkzeug
>>> client = werkzeug.Client(wsgi_app, response_wrapper=werkzeug.Response)
>>> resp=client.get("?answer=42")
>>> type(resp)
<class 'werkzeug.wrappers.Response'>
>>> resp.status
'200 OK'
>>> resp.content_type
'text/plain'
>>> print(resp.data.decode())
Hello world.
Далее используйте объект werkzeug.Response:
>>> client = werkzeug.Client(response_app, response_wrapper=werkzeug.Response)
>>> resp=client.get("?answer=42")
>>> print(resp.data.decode())
Hello world!
Класс werkzeug.Request предоставляет содержимое словаря среды (аргумент environ, расположенный над wsgi_app()) в форме, удобной для пользователя, а также декоратор для преобразования функции, которая принимает объект werkzeug.Request и возвращает werkzeug.Response приложению WSGI:
>>> @werkzeug.Request.application
… def wsgi_app_using_request(request):
… ····msg = "A WSGI app with: \n method: {}\n path: {}\n query: {}\n"
… ····return werkzeug.Response(
… ········msg.format(request.method, request.path, request.query_string))
…
Она возвращает следующий код:
>>> client = werkzeug.Client(
… ····wsgi_app_using_request, response_wrapper=werkzeug.Response)
>>> resp=client.get("?answer=42")
>>> print(resp.data.decode())
A WSGI app with:
···method: GET
···path: /
···query: b'answer=42'
Теперь мы знаем, как работать с объектами werkzeug.Request и werkzeug.Response. Помимо них, в документации указана маршрутизация. Рассмотрим фрагмент кода, где она используется; порядковые номера определяют шаблон и соответствующее ему значение.
>>> env = werkzeug.create_environ(path='/')
>>> urls = url_map.bind_to_environ(env)
>>> urls.match()
('index', {})