Comments (29)
@r0fls It works but i think use html function replace text maybe better
from sanic import Sanic
from sanic.response import html
from jinja2 import Environment, PackageLoader
env = Environment(loader=PackageLoader('app', 'templates'))
app = Sanic(__name__)
@app.route('/')
async def test(request):
data = {'name': 'name'}
template = env.get_template('index.html')
html_content = template.render(name=data["name"])
return html(html_content)
app.run(host="0.0.0.0", port=8000)
from sanic.
I think this is not necessary.
It's trivial to import jinja2
and use it if you so desire, it's not like Jinja support is so complex, or needs some sort of deep integration with Sanic, to justify being pulled into the project's core. Jinja is not even hard to use by itself.
As of right now, Sanic is perfectly simple, has minimal usage of other dependencies, and in many ways it's more of a microframework than Flask itself. You can perfectly use Jinja with Sanic right now, as well as whatever templating engine you desire, it's all one import away.
The community can also create a Jinja2 integration module for Sanic if they so desire (and it's really easy to do so). The same holds true for any other templating system or format they want to use in the future.
The alternative to people who don't want to use Jinja is not as pleasing. Adding Jinja as a dependency will have everyone who intends to use Sanic as a backend tool only installing Jinja. It will also have people who want to use other templating engines be forced into having Jinja in their dependencies even though they will never use it.
From a development perspective, adding Jinja would only serve to enlarge a beautifully small core. It's also more surface to maintain, and a detraction from the idea of a microframework in my opinion. Just look at the Frankenstein that is Flask nowadays -- crossed imports from werkzeug
, multiple interfaces that accomplish the same thing, no standard way of doing things, not to mention the gigantic documentation for something that is supposed to be micro.
This project has everything going for it, and a growing community, at a rate that is impressive for such a new project. Personally what drove me to this is the small footprint, in all regards. I'd much rather see the community build on top of this and keep the core small, rather than just request the devs to build everything into the core. Especially something like this which is simple and probably wouldn't take more than 100 LOC to build.
My vote is for keeping Sanic sane, slim, pluggable, and performant. And build a vibrant community of modules around it π
from sanic.
In case anyone is confused, you can use jinja2 currently with sanic:
from jinja2 import Template
from sanic import Sanic
from sanic.response import text
template = Template('Hello {{ name }}!')
app = Sanic()
@app.route("/")
async def test(request):
data = request.json
return text(template.render(name=data["name"]))
app.run(host="0.0.0.0", port=8000)
Then, to see it working:
curl -d '{"name": "r0fls"}' localhost:8000
from sanic.
@r0fls That would actually be a great thing to include in examples
from sanic.
Templating will not be built into Sanic core to keep the core as lean and performant as possible. Thanks for everyone's input on the subject!
from sanic.
@seemethere @r0fls as I mention in the starting message it's very often you need context processors. So it will be much more code than you show. Of course it may be implemented as independent library. So question is why I propose to include it into framework. The answer is because many flask or django apps include templates. So they need some agreement where (in which folder) to locate it e.t.c. It may be implemented with independent template library too. But if there will be many such libs, it will cause problem to use apps that relied on different of these.
In the one sentence: I think built-in templates will increase popularity of sanic as it was with flask and django.
from sanic.
@Benozo why are you creating jinja environment each request?
- Try to move it into global space.
- Try to move even
env.get_emplate()
into globals. And if it will be significantly faster, try to play with jinja settings of debugging and reloading. Or just cache all the templates yourself withfunctools.lru_cache
from sanic.
If you don't mind complicating your stack a little, you can push all your template rendering client side and save it in localstorage with something like react. Then handle all the "work" rendering the template there. Only ask sanic when you need actual json data. I mean, if you gotta go fast.
That said, the class based views and models in django while slower, are really convenient.
from sanic.
π
I'll be happy even if there is only a Jinja2 support :p
from sanic.
I think Jinja2 is slow, not that is noticeable but wrk recons otherwise .
If anyone is interested here is a simple wrk bench between Jinja2 and SimpleTemplates by Bottle..
Enviroment:
Processor: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz (4 CPUs), ~2.2GHz
Memory: 8192MB RAM
Sanic running under Windows Subsystem with Ubuntu 16 Kernel
index.html = <h1>Hello Sanic</h1>
SimpleTemplate
@app.route('/')
async def index(request):
return html(tpl('index.html'))
Jinja2 Template
def template(tpl, **kwargs):
env = Environment(loader=PackageLoader('main', 'views'),autoescape=select_autoescape(['html', 'xml', 'tpl']))
template = env.get_template(tpl)
return html(template.render(kwargs))
@app.route('/')
async def index(request):
return template('index.html')
'''
Jinja2
wrk -c 100 -t 2 --latency -v http://localhost:8085/
wrk 4.0.0 [epoll] Copyright (C) 2012 Will Glozer
Running 10s test @ http://localhost:8085/
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 209.11ms 68.58ms 403.50ms 76.61%
Req/Sec 249.01 112.91 490.00 65.00%
Latency Distribution
50% 180.66ms
75% 200.99ms
90% 336.00ms
99% 374.64ms
4691 requests in 10.10s, 774.20KB read
Requests/sec: 464.66
Transfer/sec: 76.69KB
Bottle SimpleTemplate
wrk -c 100 -t 2 --latency -v http://localhost:8085/
wrk 4.0.0 [epoll] Copyright (C) 2012 Will Glozer
Running 10s test @ http://localhost:8085/
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 34.19ms 11.65ms 75.01ms 66.65%
Req/Sec 1.41k 153.44 1.77k 67.68%
Latency Distribution
50% 30.51ms
75% 43.01ms
90% 52.32ms
99% 61.88ms
28042 requests in 10.03s, 4.52MB read
Requests/sec: 2795.71
Transfer/sec: 461.40KB
'''
from sanic.
I'm going to close this at the end of the week, unless someone has a really good idea why we should bake jinja2 into sonic core. Jinja2 example has been added as of #193.
from sanic.
@istommao and at that point for data you could just use **data
to avoid having to rename arguments. Or if you want to get the full effect of a context_processor you could just pass **locals()
to the template.render.
I think this shows you can get the full effects of the template engine jinja2 without it having to be baked into sanic core.
from sanic.
+1
from sanic.
+1
from sanic.
+1
from sanic.
What benefit does this add besides you not needing to import the jinja2 package?
from sanic.
@seemethere I agree with you!
from sanic.
we also put that in blueprint,for example:
configure template into blueprint
/home/webapp
|-- main.py
|-- my_blueprint.py
templates
|-- index.html
1).main.py
from sanic import Sanic
from my_blueprint import bp
app = Sanic(__name__)
app.blueprint(bp)
app.run(host='0.0.0.0', port=8000, debug=True)
2).my_blueprint.py
# more my_blueprint.py
from sanic.response import json, text, html
from sanic import Blueprint
from jinja2 import Environment, PackageLoader
env = Environment(loader=PackageLoader('my_blueprint', 'templates'))
bp = Blueprint('my_blueprint')
@bp.route('/')
async def bp_root(request):
template = env.get_template('index.html')
content=template.render(title='Sanic',people='David')
return html(content)
3).index.html
<!doctype html>
<title>{{ title }}</title>
<div class=page>
<h1>hello, {{ people }}</h1>
</div>
from sanic.
i write a little plugin to use flask style render_template , if any one want i will publish it .
from sanic.
This is very easy to implement, and +1 on not including it in the Sanic core.
here is a basic function to use Jinja2 with Sanic.
from sanic import Sanic
from sanic.response import json, html, text
from jinja2 import Environment, PackageLoader, select_autoescape
env = Environment(loader=PackageLoader('main', 'templates'),autoescape=select_autoescape(['html', 'xml', 'tpl']))
def template(tpl, **kwargs):
template = env.get_template(tpl)
return html(template.render(kwargs))
app = Sanic(__name__)
app.static('/static', './static')
@app.route('/')
async def test(request):
test = {'pip':'pop','foo':'bar'}
print(test)
return template('index.html', title='Sanic',data='blob', test=test)
if __name__ == "__main__":
app.run(host='0.0.0.0',port=8085)
Index.html
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" media="screen">
<link rel="stylesheet" href="static/css/main.css">
</head>
<body>
<div class="container">
<h1>{{data}}</h1>
{% for key, value in test.item %}
{{key}} {{value}}
{% endfor %}
</div>
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
</body>
</html>
Easy as py :-) .
from sanic.
@imbolc Yes your correct after i moved the Jinja2 Environment into global space all was fast again, thanks for pointing that out. π .
from sanic import Sanic
from sanic.response import html
from bottle import template as tpl
from jinja2 import Environment, PackageLoader, select_autoescape
app = Sanic(__name__)
env = Environment(loader=PackageLoader('main', 'views'),autoescape=select_autoescape(['html', 'xml']))
@app.route('/')
async def index(request):
template = env.get_template('index.tpl')
return html(template.render())
#return html(tpl('index')) # Uncomment for Stpl bench
Bottle Stpl
wrk -c 100 -t 2 --latency -v http://localhost:8085/
wrk 4.0.0 [epoll] Copyright (C) 2012 Will Glozer
Running 10s test @ http://localhost:8085/
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 28.83ms 8.53ms 67.77ms 80.37%
Req/Sec 1.70k 100.46 1.89k 74.00%
Latency Distribution
50% 26.52ms
75% 29.00ms
90% 43.37ms
99% 54.57ms
34047 requests in 10.07s, 6.72MB read
Requests/sec: 3381.78
Transfer/sec: 683.62KB
Jinja2
wrk -c 100 -t 2 --latency -v http://localhost:8085/
wrk 4.0.0 [epoll] Copyright (C) 2012 Will Glozer
Running 10s test @ http://localhost:8085/
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 29.15ms 8.45ms 69.20ms 81.96%
Req/Sec 1.69k 97.07 1.88k 72.50%
Latency Distribution
50% 27.00ms
75% 29.00ms
90% 42.00ms
99% 55.19ms
33726 requests in 10.06s, 6.66MB read
Requests/sec: 3353.10
Transfer/sec: 677.82KB
from sanic.
Any progress regarding render_template ..?
from sanic.
@siripuramrk https://github.com/yunstanford/jinja2-sanic
from sanic.
I'm thinking about using a template engine with Sanic.
I already posted here https://stackoverflow.com/questions/46040053/sanic-template-in-async-mode-and-jinja2 and others before seeing this post.
After reading, I understand that using Jinja2 directly with its API is a good idea.
What is the interest of using the asynchronous mode of Jinja2.
First, reading the template file does not use any asynchronous system (even if aiofiles is not really asynchronous).
Then, a test bench seems to show a decrease of almost 50% in asynchronous mode ...
In your opinion what is the best practice to follow to optimize and keep Sanic's performance, including whether to change template engine?
The template : exemple_jinja2_02.html
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<div>
<h1>{{data}}</h1>
{% for key, value in test.items() %}
{{key}} {{value}} <br>
{% endfor %}
</div>
</body>
</html>
The python file :
from sanic import Sanic
from sanic.response import json, html, text
from jinja2 import Environment, PackageLoader, select_autoescape
env = Environment(loader=PackageLoader('exemple_jinja2_02', 'templates'), autoescape=select_autoescape(['html', 'xml', 'tpl']), enable_async=True)
async def template(tpl, **kwargs):
template = env.get_template(tpl)
content = await template.render_async(kwargs)
return html(content)
app = Sanic(__name__)
app.static('/static', './static')
test = {'pip': 'pop', 'foo': 'bar'}
@app.route('/')
async def index(request):
content = await template('test_jinja2_02.html', title='Sanic', data='blob', test=test)
return content
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8000, debug=True)
I tested with wrk -c 100 -t 2 --latency -v http://0.0.0.0:8000/
in mode asynchronous :
wrk 4.0.0 [epoll] Copyright (C) 2012 Will Glozer
Running 10s test @ http://0.0.0.0:8000/
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 55.45ms 5.19ms 113.79ms 97.09%
Req/Sec 0.90k 192.56 1.01k 81.50%
Latency Distribution
50% 54.53ms
75% 56.14ms
90% 57.59ms
99% 65.56ms
17992 requests in 10.01s, 13.25MB read
Requests/sec: 1797.40
Transfer/sec: 1.32MB
in mode synchronous :
wrk -c 100 -t 2 --latency -v http://0.0.0.0:8000/
wrk 4.0.0 [epoll] Copyright (C) 2012 Will Glozer
Running 10s test @ http://0.0.0.0:8000/
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 32.10ms 3.88ms 67.35ms 92.74%
Req/Sec 1.56k 211.24 2.02k 77.50%
Latency Distribution
50% 31.36ms
75% 32.75ms
90% 34.54ms
99% 54.93ms
31098 requests in 10.01s, 22.90MB read
Requests/sec: 3106.24
Transfer/sec: 2.29MB
from sanic.
I forgot, for the people who read this issue, there is an example with Jinja2 in async mode here : https://github.com/channelcat/sanic/pull/641/files
I don't know why but this exemple is not here : https://github.com/channelcat/sanic/tree/master/examples
from sanic.
@patic-fr Those examples were moved to the wiki: https://github.com/channelcat/sanic/wiki/Examples#jinja
from sanic.
@patic-fr Reading template files in async is just a bad idea, this should be done once during program startup and then they should be kept cached in memory. Reading them every time you serve a request is just wasteful.
Spawning a coroutine for rendering is another bad idea, rendering your templates is inherently CPU bound and will put pressure on your event loop no matter where they are, there is nothing to be gained by rendering them asynchronously.
In short: Stop reading the file every time, don't render in a separate async function.
from sanic.
there is nothing to be gained by rendering them asynchronously
actually there is, imagine you have some handlers in a single process, answering different time, e.g. simple json responses (which renders milliseconds) and heavy inherited templates (which renders about a second), with async rendering json responses will become faster. Whole the system probably will become somehow slowly, but smoothly
from sanic.
@luizberti
Ok, I'm dumb, of course, the files are not read every time, there is a cache.
And when I checked the code of Jinja2, I didn't find a async method to read the file.
So, why the async mode in the exemple above with Jinja give bad result in benchmark ?
from sanic.
Related Issues (20)
- openapi json exception
- The RESTful API has redundant path parameters HOT 1
- Response streaming produces [ERROR] Invalid response type None (need HTTPResponse)
- Unexpected behavior with bp.middleware() using classes HOT 2
- δΈδΈͺζ±ε© HOT 1
- "body not consumed" error on GET request with content-length=0 HOT 1
- mypy error type for async get function in HTTPMethodView HOT 2
- Unexpected behaviour with REQUEST_MAX_HEADER_SIZE HOT 1
- Unexpected behavior when using blueprint groups and app.url_for HOT 1
- How to avoid abort when the code throw an Exception HOT 1
- Sanic drops part of HTTP response data HOT 7
- REQUEST_TIMEOUT & RESPONSE_TIMEOUT not working
- Documentation Hidden by Menu HOT 1
- Native Gzip & Brotli compression HOT 3
- AttributeError: 'Sanic' object has no attribute 'multiplexer' HOT 3
- help pls, when func A and func B are in defference Sanic-Server, same global asyncio.Event() var in them cant sync HOT 1
- TypeError: listener() got an unexpected keyword argument 'priority' HOT 1
- HTTPMethodView subclass not supports generics
- A question about request.json HOT 1
- Add sanic to crowdin
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sanic.