从python模块加载Jinja模板(预编译模板)

时间:2012-02-23 23:46:47

标签: python google-app-engine jinja2

我在App Engine上使用Python 2.5并尝试让Jinja2 ModuleLoader工作。

初始化我使用的环境:

@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'是我的GAE项目中的一个目录。 但我一直收到TemplateNotFound异常吗?

我使用以下方法编译模板:

    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()

结果代码看起来很好。有任何想法吗? 我希望你能帮助我。 Rodrigo Moraes的这篇文章表明从python模块加载模板非常快。但在2009年的概念验证中,他“入侵”了Jinja代码以便能够运行代码。我认为ModuleLoader应该做同样的工作。 https://groups.google.com/group/pocoo-libs/browse_thread/thread/748b0d2024f88f64


testmod.py看起来像这样:

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 = ''

页面处理程序:

def get(self):

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

我也尝试过没有.py扩展名的模板。

3 个答案:

答案 0 :(得分:1)

更新:使用我用于CMS的代码查看此Gist:

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

更新:现在我正在使用Python 27并且能够创建一个模块加载器,它可以从包或数据库加载jinja编译模板(python代码)(package = None)。

要初始化加载程序,您可以使用:

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 :(得分:0)

我假设您在开发环境中预先编译模板然后部署它们。 您是否确认可以在已部署的环境中访问这些文件?

答案 2 :(得分:0)

我放弃了ModuleLoader并回到了Rodrigo Moraes的解决方案。我没有“破解”Jinja2代码。我选择在生成的源代码中插入两行代码来获取环境。

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

我在ModuleLoader中更改了Rodrigo的加载函数:

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

结果看起来很有希望。