А потом появилась мысль — а не поделиться ли своими наработками с людьми? Итак представляю вашему вниманию пакет torskel.
Сразу оговорюсь, что он дружит только с Python 3.5+ т. к. во всю использует async/await.
Это обёртка над торнадо, которая позволяет получить из коробки базовый функционал:
Установка библиотеки — pip install torskel
Асинхронная работа с Redis
По умолчанию выключена, чтобы включить надо установить опцию use_redis в True и сделать
pip install aioredis
Пример использования:
import asyncio
from torskel.torskel_app import TorskelServer
from torskel.torskel_handler import TorskelHandler
import tornado.web
from tornado.options import options
options.define('use_redis', default=True, help='use redis', type=bool)
class RedisApplication(TorskelServer):
def __init__(self, handlers, **settings):
super().__init__(handlers, **settings)
self.greeting = 'Hello redis!'
class RedisHandler(TorskelHandler):
async def get(self):
my_key = self.get_hash_str('my_key')
await self.set_redis_exp_val(my_key, self.application.greeting, 3000, convert_to_json=False)
res = await self.get_redis_val(my_key, from_json=False)
self.write(res)
await self.del_redis_val(my_key)
self.finish()
redis_app = RedisApplication(handlers=[(r"/", RedisHandler)])
if __name__ == '__main__':
redis_app.listen(8888)
loop = asyncio.get_event_loop()
redis_app.init_with_loop(loop)
loop.run_forever()
tornado.ioloop.IOLoop.instance().start()
Список доступных опций со значениями по умолчанию:
Определяет надо ли вообще использовать редис
use_redis=False
По умолчанию подключение через сокет файл
use_redis_socket=True
Создаем пул коннектов — минимум 5, максимум 10
redis_min_con=5
redis_max_con=10
Как попасть в редис?
redis_host='127.0.0.1'
redis_port=6379
redis_socket='/var/run/redis/redis.sock'
Настройка авторизации
redis_psw=''
База по умолчанию
redis_db=1
Шлём логи на почту
Настраивается следующими опциями:
options.define('use_mail_logging', default=False, help='SMTP log handler', type=bool)
options.define("log_mail_subj", default='', type=str)
options.define("log_mail_from", default='', type=str)
options.define("log_mail_to", default=[], type=list)
options.define("log_mail_host", default='', type=str)
options.define("log_mail_user", default='', type=str)
options.define("log_mail_psw", default='', type=str)
Думаю, здесь особых пояснений не нужно, названия опций говорят сами за себя.
Http запросы
Просто обёртка над стандартным торнадовским AsyncHttpClient
import tornado.web
from torskel.torskel_app import TorskelServer
from torskel.torskel_handler i mport TorskelHandler
class HelloHttpHandler(TorskelHandler):
async def get(self):
res = await self.http_request_get('http://example.com')
self.write(res)
self.finish()
hello_http_app = TorskelServer(handlers=[(r"/", HelloHttpHandler)])
if __name__ == '__main__':
hello_http_app.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Поддержка ReactJS
Включается опцией use_reactjs. Так же надо сделать pip install jinja2 Здесь нам уже понадобится набор js-разработчика, npm, webpack/gulp и прочие babel'ы
Рендер происходит функцией
react_render
. Предполагается, что вашем html шаблоне, вы подключаете скрипт строчкой <script src="{{ assets['main']['js'] }}"> </script>
Этот момент довольно тонкий и в будущем я планирую его доработать.
import tornado.web
import os
from tornado.web import url
from tornado.options import options, define
from torskel.torskel_app import TorskelServer
from torskel.torskel_handler import TorskelHandler
settings = {}
options.define('use_reactjs', default=True, help='use reactjs', type=bool)
options.define("react_assets_file", default='webpack-assets.json', type=str)
class MainHandler(TorskelHandler):
def get(self):
self.react_render('index.html')
self.finish()
handlers = [
url(r"/", MainHandler, name="IndexPage"),
]
class HelloReactApplication(TorskelServer):
def __init__(self, handlers, **settings):
super().__init__(handlers, **settings)
hello_react = HelloReactApplication(handlers, root_dir=os.path.dirname(__file__), **settings)
if __name__ == "__main__":
hello_react.listen(options.port)
tornado.ioloop.IOLoop.current().start()
Это пока альфа-версия библиотеки. В планах еще добавить поддержку создания пулов коннектов к различным базам данных.
PS
Ссылка на гитхаб
github.com/frostspb/torskel
Комментарии (6)
frostspb Автор
23.11.2017 17:52Обновил, добавил ссылку на гитхаб в конце. скопирую и сюда
github.com/frostspb/torskelresetme
23.11.2017 18:34Спасибо, посмотрел. Маленький ревью кода:
- Не ловите все исключения, а только конкретные, те которые нужно отработать;
- Не оборачивайте logger во вспомогательные методы или функции, используйте его напрямую;
- Лучше сразу упасть при проблеме с импортом, чем потом когда приложение инициализируется;
- Надо следить за строгостью оформления кода по PEP8;
- Табы в коде Python — это зло;
- Если нацелены на большой охват аудитории, то не пишите докстринги на русском;
Если не понятен какой-либо пункт, я постараюсь разъяснить отдельно.
На мой взгляд, код пакета похож на обертку для кода, чем на удобный в использовании инструмент. Если делать почти одинаковые сервисы с использованием этого пакета, то с ростом количества сервисов сложнее будет поддерживать совместимость с этим пакетом. Проще перенести весь этот код в код сервиса и использовать там, благо здесь не много кода.
Если есть вопросы, я с удовольствием отвечу.
frostspb Автор
23.11.2017 20:54Спасибо, за отзыв. Есть вопросы.
2 — почему оборачивать логгер плохо?
3 — тут расчёт на то, что может понадобиться просто приложение, без подключенного функционала редиса/реакта, то есть получается что импортировать тот же aioredis необязательно
по 4,5,6 — вопросов нет, так вышло, тоже буду исправлять в будущем.
foldr
24.11.2017 11:313 — следствие того, что Вы в одной библиотеке пытаетесь собрать разный, несвязанный функционал. Слышали такое понятие Unix-way?
Пишите программы, которые делают что-то одно и делают это хорошо
То же применимо к библиотеке. Обертка для редиса — одно, для реакта — другое, логирование — третье. Тогда, если мне нужен будет какой-то функционал, я сделаю
pip install torskel-redis
,
и не нужны будут костыли вродеuse_redis=False
. Соответственно, у разных библиотек должны быть разные setup файлы со своими зависимостями
1 — добавлю про исключения. Не делайте
try .. except
всех исключений. Почему — см. на sof
resetme
24.11.2017 20:452 — почему оборачивать логгер плохо?
Он уже готов к использованию, не нужно оберток, он и так простой. Где-то в начале файла мы его получаем и используем в любом месте.
3 — тут расчёт на то, что может понадобиться просто приложение, без подключенного функционала редиса/реакта, то есть получается что импортировать тот же aioredis необязательно
Вам редко надо будет включать или отключать Redis в приложении. Проще aioredis сразу установить и импортировать. Иначе вам придется постоянно проверять переменную на None, если она не пустая только тогда вызывать функцию из пакета.
Вот этим куском вы как бы эмулируете стандартное поведение Python при импорте без обработки исключения ImportError:
try: import aioredis except ImportError: aioredis = None ... if aioredis is None: raise ImportError('Required package aioredis is missing') else: self.init_redis_pool(loop) ...
resetme
Хотел бы ознакомиться с исходным кодом, но ссылку на репозитарий библиотеки в статье не нашел. Не могли бы дать?