-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add templates using jinja2 #107
Conversation
Codecov Report
@@ Coverage Diff @@
## main #107 +/- ##
=======================================
Coverage 98.45% 98.45%
=======================================
Files 364 366 +2
Lines 26989 27023 +34
=======================================
+ Hits 26571 26605 +34
Misses 418 418
Flags with carried forward coverage won't be shown. Click here to find out more.
This change has been scanned for critical changes. Learn more |
Codecov Report
@@ Coverage Diff @@
## main #107 +/- ##
=======================================
Coverage 93.19% 93.20%
=======================================
Files 341 343 +2
Lines 26645 26679 +34
=======================================
+ Hits 24832 24866 +34
Misses 1813 1813
Flags with carried forward coverage won't be shown. Click here to find out more.
|
8fd1534
to
838f924
Compare
Codecov Report
@@ Coverage Diff @@
## main #107 +/- ##
=======================================
Coverage 93.19% 93.20%
=======================================
Files 341 343 +2
Lines 26645 26679 +34
=======================================
+ Hits 24832 24866 +34
Misses 1813 1813
Flags with carried forward coverage won't be shown. Click here to find out more.
|
838f924
to
3c05125
Compare
services/template.py
Outdated
return _get_cached_template_service() | ||
|
||
|
||
def _get_cached_template_service(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this to be a singleton?...
I ask because it doesn't seem to be a pattern we use much with services
in the worker, and I don't know if it provides benefits to implement it like that.
My real concern is that we had issues before with tests (read ATS) around the cached ENV.
I'm not opposed to this idea, I'm just curious what are the benefits of doing it as a singleton. Does the loading process takes long? Is that recommended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it has to be, I guess it does add complexity for no real reason, i just thought it's probably an object that we don't need to instantiate a bunch of times, it's sort of just like load it and have it stay the same until we redeploy, but you're correct that we don't need it to be a singleton
3c05125
to
73c8150
Compare
services/template.py
Outdated
from jinja2 import Environment, PackageLoader, StrictUndefined, select_autoescape | ||
|
||
|
||
def get_template_service(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is unnecessary. We can very well just instantiate TemplateService()
directly
services/template.py
Outdated
undefined=StrictUndefined, | ||
) | ||
|
||
def get_template(self, name, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] change "name" to "template_name".
[less nit]
Personally I don't love that we return a rendered template from this function. I think that this function should simply get and return the template we specified, AND handle errors when we fail to get the template (like you ask for one that doesn't exist)
Then we modify the template object as we see fit and render it ourselves later on.
So I do think the responsibilities of this function should be limited to fetching a template and returning it (as the name suggests) and handling errors.
from services.template import get_template_service | ||
|
||
|
||
class TestTemplate(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see a test where you ask for a template that doesn't exist
73c8150
to
471e7f4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all good to me, except unfortunately i have to give you whiplash on the singleton issue :v
i googled "jinja environment expensive" and learned you were on the right track making it a singleton before. i think gio's feedback about following the FooService().bar()
convention of other services in our code is also valid, but we can bridge the gap under the hood
services/template.py
Outdated
class TemplateService: | ||
def __init__(self): | ||
# this loads the templates from the templates directory in this repository since it's looking for a dir named `templates` next to app.py | ||
self.env = Environment( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, regarding using a singleton here:
https://jinja.palletsprojects.com/en/3.0.x/api/
Most applications will create one Environment object on application initialization and use that to load templates. In some cases however, it’s useful to have multiple environments side by side, if different configurations are in use.
https://stackoverflow.com/questions/618827/optimizing-jinja2-environment-creation
My application is running on Google App Engine and most of requests constantly gets yellow flag due to high CPU usage. Using profiler I tracked the issue down to the routine of creating jinja2.Environment instance.
it looks like creating an Environment
is expensive so we should try to only do it once. either that or we precompile templates with something like https://github.com/MiCHiLU/jinja2-precompiler but that approach doesn't seem well-documented.
i don't have a strong opinion on how to accomplish this. you could make env
a global or class-level variable instead of an instance variable and be done with it, or you could make TemplateService
a real singleton again. if you go the singleton route, there's a way to make it a singleton that you still invoke like TemplateService().get_template()
: https://code.activestate.com/recipes/66531/ (maybe not technically a singleton but splitting hairs)
@giovanni-guidini what were the "cached ENV" issues you alluded to? were those, like, env vars or specifically a jinja2.Environment
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks great, tiny tweak!
services/template.py
Outdated
|
||
def __init__(self): | ||
# this loads the templates from the templates directory in this repository since it's looking for a dir named `templates` next to app.py | ||
TemplateService.env = Environment( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will reinitialize TemplateService.env
each time we create an instance, we should only initialize it if it's still None
This commit creates the TemplateService that uses jinja to get and render templates. This TemplateService provides the get_template method to get a rendered template. It takes the file name of the template as an arg and the template variables as the kwargs. Templates should be added into the templates directory at the top level of the repository. This commit creates two example templates. Signed-off-by: joseph-sentry <[email protected]>
Signed-off-by: joseph-sentry <[email protected]>
Signed-off-by: joseph-sentry <[email protected]>
Signed-off-by: joseph-sentry <[email protected]>
Signed-off-by: joseph-sentry <[email protected]>
Signed-off-by: joseph-sentry <[email protected]>
Signed-off-by: joseph-sentry <[email protected]>
24aca61
to
e570cc6
Compare
This commit creates the TemplateService that uses
jinja to get and render templates.
This TemplateService provides the get_template method to get a rendered template. It takes the file name of the template as an arg and the template variables as the kwargs.
Templates should be added into the templates directory at the top level of the repository. This commit creates two example templates.