Кратко про классную библиотеку для создания веб-приложений в Python — Tornado

от автора

Привет, Хабр!

Сегодня мы рассмотрим библиотеку для создания веб-приложений на 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *