"""
Subclass the Jinja2 loaders. It is recommended that even for loaders that are
simple empty subclasses of the Jinja2 loaders, that these static redefinitions
be used for static template engines insteead. If in the future, changes to
these loaders need to be made to keep code working with renderstatic that will
be transparent to users.
:ref:`Jinja2 Loaders <jinja:loaders>`
"""
from os.path import normpath
from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
Callable,
Generator,
List,
MutableMapping,
Optional,
Tuple,
Union,
)
from jinja2.exceptions import TemplateNotFound
from jinja2.loaders import (
BaseLoader,
ChoiceLoader,
DictLoader,
FileSystemLoader,
FunctionLoader,
ModuleLoader,
PackageLoader,
PrefixLoader,
)
from render_static.loaders.mixins import BatchLoaderMixin
if TYPE_CHECKING: # pragma: no cover
from jinja2 import Environment, Template
__all__ = [
"StaticFileSystemLoader",
"StaticFileSystemBatchLoader",
"StaticPackageLoader",
"StaticPrefixLoader",
"StaticFunctionLoader",
"StaticDictLoader",
"StaticChoiceLoader",
"StaticModuleLoader",
]
[docs]
class SearchableLoader(BaseLoader):
"""
Loaders should implement this protocol to support shell tab-completion.
"""
def search(
self, environment: "Environment", prefix: str
) -> Generator["Template", None, None]:
"""
Search for templates matching the selector pattern.
:param selector: A glob pattern, or file name
:yield: Yields templates matching the incomplete selector prefix
"""
try:
for template in self.list_templates():
if template.startswith(prefix):
try:
yield self.load(environment, template)
except TemplateNotFound: # pragma: no cover
continue
except (TypeError, AttributeError): # pragma: no cover
pass
[docs]
class StaticFileSystemLoader(SearchableLoader, FileSystemLoader):
"""
We adapt the base loader to support loading directories as templates.
"""
is_dir: bool = False
def load(
self,
environment: "Environment",
name: str,
globals: Optional[MutableMapping[str, Any]] = None,
) -> "Template":
"""
Wrap load so we can tag directory templates with is_dir.
"""
tmpl = super().load(environment, name, globals)
setattr(tmpl, "is_dir", self.is_dir)
return tmpl
def get_source(
self, environment: "Environment", template: str
) -> Tuple[str, str, Callable[[], bool]]:
"""
Wrap get_source and handle the case where the template is
a directory.
"""
try:
self.is_dir = False
return super().get_source(environment, template)
except TemplateNotFound:
for search_path in self.searchpath:
pth = Path(search_path) / template
if pth.is_dir():
self.is_dir = True
return ("", normpath(pth), lambda: True) # pragma: no cover
raise
[docs]
class StaticFileSystemBatchLoader(StaticFileSystemLoader, BatchLoaderMixin):
"""
This loader extends the basic StaticFileSystemLoader to work with batch
selectors. Use this loader if you want to be able to use wildcards to load
Jinja2 templates.
.. note::
This is the default loader used for the Jinja2 backend if no loader is
specified.
"""
def get_dirs(self) -> List[Union[str, Path]]:
return self.searchpath # type: ignore
[docs]
class StaticPackageLoader(SearchableLoader, PackageLoader):
pass
[docs]
class StaticPrefixLoader(SearchableLoader, PrefixLoader):
pass
[docs]
class StaticFunctionLoader(FunctionLoader):
pass
[docs]
class StaticDictLoader(SearchableLoader, DictLoader):
pass
[docs]
class StaticChoiceLoader(SearchableLoader, ChoiceLoader):
pass
[docs]
class StaticModuleLoader(ModuleLoader):
pass