Search code examples
pythonpython-3.xpyqt5jinja2qresource

Load Jinja2 templates from Qt Resource System


I'm trying to load some Jinja2 templates from the Qt Resource System. I'm using Python 3.8.3, Jinja 2.11.2 and PyQt 5.15.

The problem is that I can't even detect the qrc paths to load the templates. QRC only works with Qt classes?

Python code:

from jinja2 import Environment, FileSystemLoader

file_loader = FileSystemLoader(":/plantillas/")  # Here is my problem, how I should detect the qrc path?
env = Environment(loader=file_loader)
plantilla = env.get_template("base.md")

qrc:

<!DOCTYPE RCC>
<RCC version="1.0">
    <qresource prefix="plantillas">
        <file alias="base.md">plantillas/base.md</file>
    </qresource>
</RCC>

Solution

  • The QResource only works in the Qt world so a possible solution is to create a Loader using the Qt classes:

    qloader.py

    import os
    from collections import abc
    
    from jinja2.loaders import BaseLoader, split_template_path
    from jinja2.exceptions import TemplateNotFound
    
    from PyQt5.QtCore import QDir, QDirIterator, QFile, QFileInfo, QIODevice
    
    
    class QFileSystemLoader(BaseLoader):
        def __init__(self, searchpath, encoding="utf-8", followlinks=False):
            if not isinstance(searchpath, abc.Iterable) or isinstance(searchpath, str):
                searchpath = [searchpath]
    
            self.searchpath = list(searchpath)
            self.encoding = encoding
            self.followlinks = followlinks
    
        def get_source(self, environment, template):
            pieces = split_template_path(template)
            for searchpath in self.searchpath:
                filename = os.path.join(searchpath, *pieces)
    
                f = QFile(filename)
                if not f.exists():
                    continue
                if not f.open(QIODevice.ReadOnly):
                    continue
                contents = f.readAll().data().decode(self.encoding)
                f.close()
    
                dt = QFileInfo(f).fileTime(QFile.FileModificationTime)
    
                def uptodate():
                    return QFileInfo(filename).fileTime(QFile.FileModificationTime) == dt
    
                return contents, filename, uptodate
            raise TemplateNotFound(template)
    
        def list_templates(self):
            found = set()
            for searchpath in self.searchpath:
                d = QDir(searchpath)
                it_flag = QDirIterator.Subdirectories
                if self.followlinks:
                    it_flag |= QDirIterator.FollowSymlinks
                it_filter = QDir.Files | QDir.NoDotAndDotDot | QDir.Hidden | QDir.Readable
                if not self.followlinks:
                    it_filter |= QDir.NoSymLinks
                it = QDirIterator(searchpath, it_filter, it_flag)
                while it.hasNext():
                    it.next()
                    found.add(d.relativeFilePath(it.filePath()))
            return sorted(found)
    
    from qloader import QFileSystemLoader
    
    
    qfile_loader = QFileSystemLoader(":/plantillas/")
    env = Environment(loader=qfile_loader)
    plantilla = env.get_template("base.md")