jinja_env.py
Jinja2 Environment Plugin
Provides a centralized Jinja2 environment configuration for consistent template rendering across all Markata plugins. This plugin ensures template rendering behavior is consistent and available even when specific template-using plugins are not enabled.
Installation
This plugin is built-in and enabled by default through the 'default' plugin. If you want to be explicit, you can add it to your list of plugins:
hooks = [ "markata.plugins.jinja_env", ]
Uninstallation
Since this plugin is included in the default plugin set, to disable it you must explicitly add it to the disabled_hooks list if you are using the 'default' plugin:
disabled_hooks = [ "markata.plugins.jinja_env", ]
Configuration
Configure Jinja environment settings in your markata.toml
:
[markata.jinja_env] template_paths = ["templates"] # Additional template paths to search undefined_silent = true # Return empty string for undefined variables trim_blocks = true # Remove first newline after block lstrip_blocks = true # Strip tabs/spaces from start of line template_cache_dir = ".markata.cache/template_bytecode"
Usage
The environment is automatically available to other plugins via markata.config.jinja_env
.
Template loading follows this order:
- Package templates (built-in Markata templates)
- User template paths (configured via template_paths)
Example usage in a plugin:
def render_template(markata, content): template = markata.jinja_env.from_string(content) return template.render(markata=markata)
Notes
- Template paths are resolved relative to the current working directory
- Package templates are always available and take precedence
- Silent undefined behavior means undefined variables render as empty strings
Class
_SilentUndefined class
Custom undefined type that returns empty string for undefined variables.
_SilentUndefined source
class _SilentUndefined(jinja2.Undefined): """Custom undefined type that returns empty string for undefined variables.""" def _fail_with_undefined_error(self, *args, **kwargs): return "" __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = __truediv__ = ( __rtruediv__ ) = __floordiv__ = __rfloordiv__ = __mod__ = __rmod__ = __pos__ = __neg__ = ( __call__ ) = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = __float__ = ( __complex__ ) = __pow__ = __rpow__ = _fail_with_undefined_error
Class
MarkataTemplateCache class
Template bytecode cache for improved performance.
MarkataTemplateCache source
class MarkataTemplateCache(jinja2.BytecodeCache): """Template bytecode cache for improved performance.""" def __init__(self, directory): self.directory = Path(directory) self.directory.mkdir(parents=True, exist_ok=True) def load_bytecode(self, bucket): filename = self.directory / f"{bucket.key}.cache" if filename.exists(): with open(filename, "rb") as f: bucket.bytecode_from_string(f.read()) def dump_bytecode(self, bucket): filename = self.directory / f"{bucket.key}.cache" with open(filename, "wb") as f: f.write(bucket.bytecode_to_string())
Class
JinjaEnvConfig class
Configuration for the Jinja environment.
JinjaEnvConfig source
class JinjaEnvConfig(pydantic.BaseModel): """Configuration for the Jinja environment.""" templates_dir: List[str] = [] undefined_silent: bool = True trim_blocks: bool = True lstrip_blocks: bool = True template_cache_dir: Path = Path(".markata.cache/template_bytecode") model_config = pydantic.ConfigDict( validate_assignment=True, # Config model arbitrary_types_allowed=True, extra="allow", str_strip_whitespace=True, validate_default=True, coerce_numbers_to_str=True, populate_by_name=True, )
Function
config_model function
Register configuration models.
config_model source
def config_model(markata: "Markata") -> None: """Register configuration models.""" markata.config_models.append(JinjaEnvConfig)
Function
configure function
Initialize and configure the Jinja2 environment for Markata.
This hook runs early in the configuration stage to ensure the jinja environment is available for other plugins that need it during configuration.
Args: markata: The Markata instance
configure source
def configure(markata: Markata) -> None: """Initialize and configure the Jinja2 environment for Markata. This hook runs early in the configuration stage to ensure the jinja environment is available for other plugins that need it during configuration. Args: markata: The Markata instance """ # Get configuration, falling back to defaults config = JinjaEnvConfig() if hasattr(markata.config, "jinja_env"): if isinstance(markata.config.jinja_env, dict): config = JinjaEnvConfig(**markata.config.jinja_env) # TODO: setting up env twice could not get dynamic templates to be recognized on first pass loaders = [] if markata.config.templates_dir: for path in markata.config.templates_dir: path = Path(path).expanduser().resolve() if path.exists(): loaders.append(FileSystemLoader(str(path))) # Create environment env_for_dynamic_render = Environment( loader=ChoiceLoader(loaders), undefined=_SilentUndefined if config.undefined_silent else jinja2.Undefined, trim_blocks=config.trim_blocks, lstrip_blocks=config.lstrip_blocks, bytecode_cache=MarkataTemplateCache(config.template_cache_dir), auto_reload=True, ) markata.config.dynamic_templates_dir.mkdir(parents=True, exist_ok=True) head_template = markata.config.dynamic_templates_dir / "head.html" head_template.write_text( env_for_dynamic_render.get_template("dynamic_head.html").render( {"markata": markata} ), ) # Set up loaders loaders = [] # Add package templates first (lowest priority) # loaders.append(PackageLoader("markata", "templates")) # Add user template paths (medium priority) if markata.config.templates_dir: for path in markata.config.templates_dir: path = Path(path).expanduser().resolve() if path.exists(): loaders.append(FileSystemLoader(str(path))) # Add dynamic templates directory (highest priority) # dynamic_templates_dir = Path(".markata.cache/templates") # dynamic_templates_dir.mkdir(parents=True, exist_ok=True) # loaders.append(FileSystemLoader(str(dynamic_templates_dir))) # Create environment env = Environment( loader=ChoiceLoader(loaders), undefined=_SilentUndefined if config.undefined_silent else jinja2.Undefined, trim_blocks=config.trim_blocks, lstrip_blocks=config.lstrip_blocks, bytecode_cache=MarkataTemplateCache(config.template_cache_dir), auto_reload=True, ) # Register the environment on the config's private attribute markata.jinja_env = env