Backslant – шаблонизатор в стиле slim
Захотелось мне сделать шаблонизатор, чтобы как slim, теги чтобы автоматом закрывались и прочее. Красиво же так: html head title — yield «Плюшка!» + » Чашка чаю!» Но и этого мне мало, хочу чтобы не было своего недоязыка, хочу чтобы просто питоновские конструкции. А кто захочет себе в ногу стрельнуть и бизнес логики в шаблоны навалить, то это проблема начинашек, мне зачем мучаться размазывая код вьюх в папки типа utils, template_tags и прочее?
А и еще можно кстати угореть так уж угореть —, а пусть шаблоны через новый механизм импорта в python 3 тянутся. И если надо что-то от другого шаблона себе вставить, то тоже пусть также работает.
А еще, еще пусть каждый шаблон это генератор!
Ну сказано сделано, встречайте github.com/Deepwalker/backslant. Он еще конечно не до конца допилен, но надо получить фидбек.
Итак, попробуем на пять, base.bs:
! doctype/ html html head title » Page Title body h1 {'class': ' '.join (['main', 'content'], 'ng-app': 'Application'} » Page Header div.content — yield from options['content_block']() div.footer » Backslant © 2015 Что тут у нас — doctype заканчивается на /, значит тег закрывать через не надо.
Строки пока начинаются с », надо допилить грамматику чтобы можно было сразу после тела тега, попозже.
У h1 аргументы передаются обычным python dict, в рамках которого любой код, который можно в объявлении словаря.
Дальше интересное — yield from из вызова некоего content_block, который лежит в каком-то options. Ну что сказать — options это kwargs, так как объявления параметров шаблона у нас тут нет. Может и зря кстати что нет.
Так вот, про content_block — тут мы ожидаем что нам передадут в параметре некий колбек, и считаем что там будет генератор — у нас же все шаблоны генераторы. Вот значит какой-то шаблон захочет использовать наш base.bs, и вызовет его render, и передаст туда колбек.
И это будет index.bs:
— from. import base : call base.render (*options) : content_block — for i in range (10): p — yield 'Paragraph {}'.format (i) : footer_block p » Index page Тут мы используем немножко сахара вместо того чтобы честно объявить просто функцию и передать её. : call переберет свои дочерние ноды, проверит что все они объявления функций, и засунет их в параметры. А : content_block как раз и объявляет функцию без аргументов с именем content_block, и с этим же именем : call отправит ее в аргументы.А потом в питоно коде можем использовать:
import backslant sys.meta_path.insert (0, backslant.PymlFinder ('./templates', hook='backslant_import')) from backslant_import.home import index for chunk in index.render (title='The Real Thing'): print (chunk) Безумненько. Что добавить по синтаксису — функцию объявить можно, прям — def func (a=True) и прочее. for, if, elif, else — просто чистый питон. Конечно же можно и нужно использовать yield и yield from. Можно импортировать всё что угодно и как угодно использовать.Из неподдержанного — try: except: … Текущая версия парсера не очень дружит, надо переделать парсинг.
Что дальше — генератор же. А генератор как известно еще и send умеет, не только next. Правда что из этого можно получить, ну не знаю, можно пофантазировать. Может как-то докармливать данными и отдавать порции на выход.
Скорость — такая же как у jinja2. Можно наверное попробовать как-то разогнать еще, но в основном код состоит из yield и yield from, компилируется через ast, нечего особо оптимизировать.
Так вот. Синтаксис можно еще допилить, внедрить какие-то идеи.
Есть кстати идеи? Давайте обсудим. А пока можно посмотреть на проект, потыркать примеры github.com/Deepwalker/backslant/tree/master/example