Привет, Хабр!
Сегодня мы рассмотрим библиотеку для создания веб-приложений на Python — Tornado.
Tornado был разработан в компании FriendFeed, которая позже была приобретена Facebook в 2009 году. Основная идея создания Tornado заключалась в высокой производительности и масштабируемости при обработке большого числа одновременных соединений. Этого удалось достичь с помощью асинхронности.
Установим Tornado:
pip install tornado
Основные компоненты Tornado
RequestHandler и обработка запросов
RequestHandler
— это основной класс в Tornado для обработки HTTP-запросов. Каждый запрос обрабатывается экземпляром класса RequestHandler
, который выполняет необходимые действия и формирует ответ.
Пример:
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") def post(self): name = self.get_argument('name') self.write(f"Hello, {name}") def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
MainHandler
обрабатывает GET и POST запросы. Метод get
возвращает строку «Hello, world», а метод post
принимает аргумент name
из тела запроса и возвращает персонализированное приветствие.
RequestHandler
содержит множество полезных методов для работы с HTTP-запросами и формирования ответов:
-
get_argument(name, default=None, strip=True)
— получает аргумент из строки запроса или тела запроса. -
get_arguments(name, strip=True)
— получает список аргументов. -
write(chunk)
— записывает данные в ответ. -
render(template_name, **kwargs)
— рендерит HTML-шаблон с переданными параметрами. -
set_header(name, value)
— устанавливает HTTP-заголовок. -
set_status(status_code, reason=None)
— устанавливает статус ответа.
Маршрутизация
Маршрутизация в Tornado настраивается с помощью класса Application
, который связывает URL-шаблоны с соответствующими обработчиками.
Пример:
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("This is the main page") class AboutHandler(tornado.web.RequestHandler): def get(self): self.write("This is the about page") def make_app(): return tornado.web.Application([ (r"/", MainHandler), (r"/about", AboutHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Здесь определили два маршрута: корневой URL («/») обрабатывается MainHandler
, а URL «/about» обрабатывается AboutHandler
.
Маршрутизация в Tornado определяется с помощью списка кортежей, где каждый кортеж содержит шаблон URL и соответствующий обработчик:
application = tornado.web.Application([ (r"/", MainHandler), (r"/about", AboutHandler), ])
Шаблоны
Tornado поддерживает систему шаблонов для генерации динамических HTML-страниц. Шаблоны позволяют отделить логику приложения от представления.
Пример:
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html", title="Home Page", items=["Item 1", "Item 2", "Item 3"]) def make_app(): return tornado.web.Application([ (r"/", MainHandler), ], template_path="templates") if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Шаблон index.html
:
<!DOCTYPE html> <html> <head> <title>{{ title }}</title> </head> <body> <h1>{{ title }}</h1> <ul> {% for item in items %} <li>{{ item }}</li> {% end %} </ul> </body> </html>
Здесь MainHandler
использует метод render
для генерации HTML-ответа на основе шаблона index.html
, передавая в него переменные title
и items
.
Tornado использует собственный язык шаблонов, который включает базовые конструкции для вставки данных, циклов и условных выражений. Шаблоны хранятся в отдельной директории, указанной в параметре template_path
при создании приложения.
Асинхронное программирование в Tornado
Асинхронное программирование — это фича Tornado, позволяющая создавать масштабируемые веб-приложения. Tornado использует неблокирующий ввод-вывод и event loop для обработки большого количества одновременных соединений.
Асинхронное I/O позволяет обрабатывать другие задачи, пока ожидается завершение операций ввода-вывода. Tornado использует event loop, который непрерывно проверяет события и запускает соответствующие обработчики.
Пример с event loop:
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): async def get(self): await self.some_async_function() self.write("Hello, world") async def some_async_function(self): await tornado.gen.sleep(1) def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Здесь some_async_function
выполняет асинхронную операцию sleep
на одну секунду. Метод get
ждет завершения этой функции перед отправкой ответа.
Корутины и future-объекты
Корутину можно рассматривать как функцию, которую можно приостановить и возобновить. В Tornado для работы с корутинами используется модуль tornado.gen
:
import tornado.ioloop import tornado.web import tornado.gen import asyncio class MainHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): result = yield self.some_async_function() self.write(f"Result: {result}") @tornado.gen.coroutine def some_async_function(self): yield tornado.gen.sleep(1) return "Async operation complete" def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Используем tornado.gen.coroutine
для определения корутины.some_async_function
выполняет асинхронную операцию и возвращает результат.
Примеры асинхронных операций:
Асинхронные операции в Tornado включают работу с HTTP-запросами, БД и другими внешними ресурсами.
Асинхронный HTTP-запрос:
import tornado.ioloop import tornado.web import tornado.httpclient class MainHandler(tornado.web.RequestHandler): async def get(self): http_client = tornado.httpclient.AsyncHTTPClient() response = await http_client.fetch("http://example.com") self.write(response.body) def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Так можно выполнить асинхронный HTTP-запрос с использованием AsyncHTTPClient
.
Асинхронная работа с БД:
import tornado.ioloop import tornado.web import aiomysql class MainHandler(tornado.web.RequestHandler): async def get(self): pool = await aiomysql.create_pool(host='127.0.0.1', port=3306, user='root', password='', db='test', loop=tornado.ioloop.IOLoop.current().asyncio_loop) async with pool.acquire() as conn: async with conn.cursor() as cur: await cur.execute("SELECT 42;") result = await cur.fetchone() self.write(f"Result: {result[0]}") def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Используем aiomysql
для выполнения асинхронного запроса к MySQL базе данных.
Прочие возможности Tornado
WebSocket
Tornado имеет встроенную поддержку WebSocket. Пример:
import tornado.ioloop import tornado.web import tornado.websocket class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, WebSocket") class EchoWebSocket(tornado.websocket.WebSocketHandler): def open(self): print("WebSocket opened") def on_message(self, message): self.write_message(u"You said: " + message) def on_close(self): print("WebSocket closed") def make_app(): return tornado.web.Application([ (r"/", MainHandler), (r"/websocket", EchoWebSocket), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Создали простой WebSocket-сервер, который отправляет эхо-сообщения обратно клиенту. Класс EchoWebSocket
наследуется от tornado.websocket.WebSocketHandler
и переопределяет методы open
, on_message
и on_close
для обработки событий WebSocket.
Интеграция с фреймворками для аутентификации и авторизации
Tornado поддерживает интеграцию с различными фреймворками для аутентификации и авторизации, включая OAuth и OpenID.
Пример с OAuth:
import tornado.ioloop import tornado.web import tornado.auth class GoogleLoginHandler(tornado.web.RequestHandler, tornado.auth.GoogleOAuth2Mixin): async def get(self): if self.get_argument("code", False): user = await self.get_authenticated_user( redirect_uri='http://your.site.com/auth/google', code=self.get_argument("code")) self.set_secure_cookie("user", tornado.escape.json_encode(user)) self.redirect("/") else: self.authorize_redirect( redirect_uri='http://your.site.com/auth/google', client_id=self.settings["google_oauth"]["key"], scope=['profile', 'email'], response_type='code', extra_params={'approval_prompt': 'auto'}) def make_app(): return tornado.web.Application([ (r"/auth/google", GoogleLoginHandler), ], google_oauth={"key": "YOUR_CLIENT_ID", "secret": "YOUR_CLIENT_SECRET"}, cookie_secret="YOUR_COOKIE_SECRET") if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Пользователь перенаправляется на страницу авторизации Google, а затем возвращается с токеном аутентификации.
Международная поддержка и локализация
Tornado предоставляет встроенные инструменты для поддержки интернационализации i18n и локализации.
Пример кода для загрузки и использования локалей:
import tornado.ioloop import tornado.web import tornado.locale class MainHandler(tornado.web.RequestHandler): def get(self): user_locale = self.get_user_locale() greeting = self.locale.translate("Hello") self.write(greeting) def make_app(): tornado.locale.load_translations("locale/") tornado.locale.set_default_locale('en_US') return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Загружаем файлы переводов из директории locale
, а затем используем метод translate
для получения переведенной строки на основе текущей локали пользователя.
В заключение напоминаю об открытых уроках курса «Python Developer. Professional», которые скоро пройдут в Otus:
-
3 июля: Tabula rasa Python проекта. Рассмотрим best practices по настройке окружения для разработки свежего питонячьего проекта. Поговорим про всевозможные инструменты и автоматизации, которые могут применяться в таком случае. Запись по ссылке
-
16 июля: Введение в Django REST API. После завершения вебинара вы научитесь пользоваться Views и Middleware в процессе обработки запросов и ответов, а также разрабатывать RESTful API с помощью Django REST Framework. Запись по ссылке
ссылка на оригинал статьи https://habr.com/ru/articles/825696/
Добавить комментарий