📜 ⬆️ ⬇️

Backslant - slim styling engine

I wanted to make a templating system so that as slim, tags so that they would automatically close and so on. Beautiful as:
html head title - yield "!" + "  !" 


But this is not enough for me, I want it to not have its own pre-language, I just want Python constructions. And who wants to shoot himself in the foot and the business of logic in the templates, then this is a problem for the beginners, why should I suffer by smearing the code of views in utils, template_tags and so on?

And by the way, it’s possible to get angry too, and let the templates through the new import mechanism in python 3 drag on. And if you need to insert something from another template yourself, then also let it work as well.
')
And also, let each template be a generator!



Well said it is done, meet github.com/Deepwalker/backslant . Of course, it is not completely finished yet, but you need to get feedback.

So, let's try on five, 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" 


What we have here is that the doctype ends in / , which means the tag should not be closed through </doctype> .

The lines now begin with " , you need to finish the grammar so that you can immediately after the tag body, later.

At h1 arguments are passed to the usual python dict , in which any code that can be in the declaration of the dictionary.

Further interesting is the yield from the call to some content_block , which lies in some options . What can I say - options is kwargs , since we don’t have template parameter declarations here. Maybe nothing by the way what is not.

So, about content_block - here we expect that some kind of callback will be passed to us in the parameter, and we believe that there will be a generator - we have all the templates are generators. This means that some template will want to use our base.bs, and cause it to render, and give it a callback.

And it will be 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" 


Here we use a little sugar instead of honestly declaring just a function and passing it. :call will sort out its child nodes, check that all of them are function declarations, and pop them into parameters. A :content_block just declares a function without arguments with the name content_block , and with the same name :call will send it to the arguments.
And then in the python code we can use:

 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) 


Insanely. What to add by syntax - the function can be declared, directly - def func(a=True) and so on. for , if , elif , else - just a pure python. Of course, you can and should use yield and yield from . You can import anything you like and use.
From unsupported - try: except: ... The current version of the parser is not very friendly, it is necessary to redo the parsing.

What's next is the generator. And the generator as it is also known is able to send , not only next . The truth is that from this you can get, well, I do not know, you can dream up. It can somehow feed data and give portions to the output.

The speed is the same as for jinja2. You can probably try to overclock something else, but basically the code consists of yield and yield from , compiled via ast , there is nothing to optimize.

So here. You can still finish the syntax, implement some ideas.

Any ideas by the way? Let's discuss. In the meantime, you can look at the project, try examples github.com/Deepwalker/backslant/tree/master/example

Source: https://habr.com/ru/post/254567/


All Articles