загрузка шаблонов Jinja из модулей python (предварительно скомпилированные шаблоны)


Я использую Python 2.5 на движке приложений и попытался заставить работать модульный загрузчик Jinja2.

Для инициализации среды я использую:

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

Полученный код выглядит нормально. Есть идеи? Надеюсь, вы сможете мне помочь. Эта статья Родриго Мораеса показывает, что загрузка шаблонов из модулей python происходит очень быстро. Но в это доказательство концепции 2009 года он "взломал" код Джинджи, чтобы иметь возможность запустить код. Я думаю, что модульный загрузчик должен делать ту же работу. 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>nt<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 4

3 ответа:

Обновление: смотрите эту суть с кодом, который я использую для CMS:

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

Update : Теперь я использую 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)   

Я предполагаю, что вы предварительно компилируете шаблоны в среде разработки, а затем развертываете их. Вы подтвердили, что файлы доступны в развернутом env ?

Я отказался от модуля загрузки и вернулся к решению Родриго Мораеса. Мне не пришлось "взламывать" код Jinja2. Я выбираю вставить две строки кода в созданный источник, чтобы получить среду.

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

И я изменил функцию нагрузки Родриго в модуле загрузки:

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
И результаты выглядят очень многообещающими.