Reputation: 11706
I'am using Python 2.5 on App Engine and tried to get the Jinja2 ModuleLoader to work.
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 a directory in my GAE project. But I receive TemplateNotFound exceptions all the time??
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 fine. Any ideas? I hope you can help me. This article from Rodrigo Moraes shows that loading templates from python modules is very fast. But in this 2009 proof of concept he "hacked" the Jinja code to be able to run the code. I think the ModuleLoader should do the same job. https://groups.google.com/group/pocoo-libs/browse_thread/thread/748b0d2024f88f64
The 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'have also tried to get the template without the .py extension.
Upvotes: 4
Views: 3582
Reputation: 11706
Update: See this Gist with the code I use for a CMS:
https://gist.github.com/voscausa/9154936
Update : Now I am using Python 27 and was able to create a module loader, which can load jinja compiled templates (python code) from a package or from the database (package = None).
To initialize the loader, 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)
Upvotes: 1
Reputation: 11706
I gave up on the ModuleLoader and went back to the solution of Rodrigo Moraes. I did not have to "hack" the Jinja2 code. I choose 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 the 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 looks very promising.
Upvotes: 0
Reputation: 12986
I assume you a pre-compiling the templates in the dev environment then deploying them. Have you confirmed that the files are accessible in the deployed env ?
Upvotes: 0