Loading Jinja templates from python modules (precompiled templates)

I am using Python 2.5 in App Engine and trying to get JLJJ2 to work with it.

To initialize the environment, I use:

@staticmethod # get Jinja environment (global) def get_new(): # and initialize Jinja environment if myEnv._my_env == None : path = os.path.join(os.path.dirname(__file__), 'compiled') myEnv._my_env = Environment(loader = ModuleLoader(path)) return myEnv._my_env 

'compiled' is the directory in my GAE project. But I keep getting TemplateNotFound exceptions ??

I compiled the templates using:

  env = Environment(extensions=['jinja2.ext.i18n']) env.filters['euros'] = Euros db_runtimes = Runtimes.all() # the html templates saved in a db.Blob for each in db_runtimes.fetch(999) : key = each.key().name() source = db.Blob(each.content).decode('utf-8') name = key.split('.')[0] raw = env.compile(source, name=name, filename=name + '.py', raw=True) each.compiled = db.Blob(raw.encode('utf-8')) # compile and save the .py each.put() 

The resulting code looks great. Any ideas? I hope you can help me. This article by Rodrigo Moraes shows that loading templates from python modules is very fast. But in this proof of the 2009 concept, he “cracked” the Jinja code to be able to run the code. I think ModuleLoader should do the same job. https://groups.google.com/group/pocoo-libs/browse_thread/thread/748b0d2024f88f64


testmod.py looks like this:

 from __future__ import division from jinja2.runtime import LoopContext, TemplateReference, Macro, Markup, TemplateRuntimeError, missing, concat, escape, markup_join, unicode_join, to_string, identity, TemplateNotFound name = u'testmod.py' def root(context, environment=environment): if 0: yield None yield u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html xmlns="http://www.w3.org/1999/xhtml">\n<head>\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n<title>TEST</title>\n</head>\n<body>\n\t<p>test template</p>\n</body>\n</html>' blocks = {} debug_info = '' 

And the page handler:

 def get(self): my_env = myEnv.get() page = 'testmod.py' template = my_env.get_template(page) self.response.out.write(template.render({})) 

I also tried to get the template without the .py extension.

+4
source share
3 answers

Update: see this Gist with the code I use for CMS:

https://gist.github.com/voscausa/9154936

Update . Now I'm using Python 27 and I was able to create a module loader that can load compiled jinja templates (Python code) from a package or from a database (package = None).

To initialize the bootloader, you can use:

 from jinja2 import Environment Environment(auto_reload=False, loader = moduleloader.FileSystemModuleLoader(package)) class ModuleLoader(object): """Base mixin class for loaders that use pre-parsed Jinja2 templates stored as Python code. """ def get_module(self, environment, template): raise TemplateNotFound(template) def load(self, environment, filename, j_globals=None): """Loads a pre-compiled template, stored as Python code in a template module. """ if j_globals is None: j_globals = {'environment' : environment} t = object.__new__(environment.template_class) module = self.get_module(environment, filename) name, blocks, root, debug_info = module.run(environment, t) # module run function t.environment = environment t.globals = j_globals t.name = name t.filename = filename t.blocks = blocks # render function and module t.root_render_func = root t._module = None # debug and loader helpers t._debug_info = debug_info t._uptodate = lambda: True return t class FileSystemModuleLoader(ModuleLoader): def __init__(self, package = None): # load module from package or datastore self.package = package # package = "compiled" or package = None def get_module(self, environment, template): # Convert the path to a module name name = template.replace('.html', '').replace('.txt','').replace('/', '.') # NO extensions module = None if self.package == None : if name in sys.modules : return sys.modules[name] logging.info('load module : ' + name) # load module from runtimes db try : runtime = models.Runtimes.get_by_key_name(template) module_code = db.Text(runtime.compiled) module = imp.new_module(name) exec module_code in module.__dict__ sys.modules[name] = module # add to sys modules, so no import return module except (ImportError, AttributeError): logging.error('load failed : ' + name) else : # load module from package logging.info('load module : ' + name + ' from package : ' + self.package) try: mod_import = __import__(self.package, globals(), None, [str(name)]) module = getattr(mod_import, name) return module except (ImportError, AttributeError): logging.error('load failed : ' + name) raise TemplateNotFound(template) 
+1
source

I assume that you precompile the templates in a dev environment and then deploy them. Have you confirmed that the files are available in deployed env?

0
source

I abandoned ModuleLoader and returned to the decision of Rodrigo Moraes. I did not have to "crack" the Jinja2 code. I want to insert two lines of code in the generated source to get the environment.

 from templating import myEnv environment = myEnv.get() # get (and initialize) the Jinja Environment global 

And I changed Rodrigo's load function in ModuleLoader:

 def load(self, environment, filename, j_globals=None): """Loads a pre-compiled template, stored as Python code in a template module. """ if j_globals is None: j_globals = {'environment' : environment} t = object.__new__(environment.template_class) module = self.get_module(environment, filename) # name, blocks, root, debug_info = module.run(environment, t) CHANGED THE HACK t.environment = environment t.globals = j_globals t.name = module.name t.filename = filename t.blocks = module.blocks # render function and module t.root_render_func = module.root t._module = None # debug and loader helpers t._debug_info = module.debug_info t._uptodate = lambda: True return t 

And the results look very promising.

0
source

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


All Articles