Renders your markdown as a jinja template during pre_render.
The markata instance is passed into the template, giving you access to things such as all of your articles, config, and this post as post.
Examples
first we can grab a few things out of the frontmatter of this post.
### {{ post.title }}
{{ post.description }}
one-liner list of links
This one-liner will render a list of markdown links into your markdown at build time. It's quite handy to pop into posts.
{{ '\n'.join(markata.map('f"* [{title}]({slug})"', sort='slug')) }}
jinja for to markdown list of links
Sometimes quoting things like your filters are hard to do in a one line without running out of quote variants. Jinja for loops can make this much easier.
{% for post in markata.map('post', filter='"git" in tags') %}
* [{{ post.title }}]({{ post.slug }})
{% endfor %}
jinja for to html list of links
Since markdown is a superset of html, you can just render out html into your post and it is still valid.
<ul>
{% for post in markata.map('post', filter='"git" in tags') %}
<li><a href="{{ post.slug }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
Ignoring files
It is possible to ignore files by adding an ignore to your markata.jinja_md
config in your markata.toml file. This ignore follows the gitwildmatch
rules, so think of it the same as writing a gitignore.
[markata.jinja_md]
ignore=[
'jinja_md.md',
]
!!note
Docs such as this jinja_md.py file will get converted to jinja_md.md during
build time, so use .md extensions instead of .py.
Ignoring a single file
You can also ignore a single file right from the articles frontmatter, by adding jinja: false.
---
jinja: false
---
Escaping
Sometimes you want the ability to have jinja templates in a post, but also the ability to keep a raw jinja template. There are a couple of techniques that are covered mroe in the jinja docs for escaping
{% raw %}
{{ '\n'.join(markata.map('f"* [{title}]({slug})"', sort='slug')) }}
{% endraw %}
{{ '{{' }} '\n'.join(markata.map('f"* [{title}]({slug})"', sort='slug')) {{ '}}' }}
Creating a jinja extension
Here is a bit of a boilerplate example of a jinja extension.
from jinja2 import nodes
from jinja2.ext import Extension
class ExampleExtension(Extension):
tags = {"example"}
def __init__(self, environment):
super().__init__(environment)
def parse(self, parser):
line_number = next(parser.stream).lineno
arg = [parser.parse_expression()]
return nodes.CallBlock(self.call_method("run", arg), [], [], "").set_lineno(
line_number
)
def run(self, arg, caller):
return f'hello {arg}'
So that markata picks up your extension, you will need to register an
entrypoint named markata.jinja_md. Once installed markata will automatically
load this extension to its list of jinja extensions.
[project.entry-points."markata.jinja_md"]
markta_gh = "example_extension:ExampleExtension"
Once you have your extension created and ready to use you can use it in your markdown.
{% example world %}
register_jinja_extensions function
Gets jinja extensions from entrypoints and loads them in.
Returns: List of jinja Extensions
register_jinja_extensions source
def register_jinja_extensions(config: dict) -> List[Extension]:
"""
Gets jinja extensions from entrypoints and loads them in.
Returns: List of jinja Extensions
"""
return [
ep.load() for ep in pkg_resources.iter_entry_points(group="markata.jinja_md")
]
IncludeRawExtension class
None
IncludeRawExtension source
class IncludeRawExtension(Extension):
tags = {"include_raw"}
def parse(self, parser):
line_number = next(parser.stream).lineno
file = [parser.parse_expression()]
return nodes.CallBlock(
self.call_method("_read_file", file), [], [], ""
).set_lineno(line_number)
def _read_file(self, file, caller):
return Path(file).read_text()
_SilentUndefined class
silence undefined variable errors in jinja templates.
Example
template = '{{ variable }}'
article.content = Template( template, undefined=_SilentUndefined).render()
_SilentUndefined source
class _SilentUndefined(Undefined):
"""
silence undefined variable errors in jinja templates.
### Example
```python
template = '{{ variable }}'
article.content = Template( template, undefined=_SilentUndefined).render()
```
"""
def _fail_with_undefined_error(self, *args, **kwargs):
return ""
PostTemplateSyntaxError class
Custom error message for post template syntax errors.
PostTemplateSyntaxError source
class PostTemplateSyntaxError(TemplateSyntaxError):
"""
Custom error message for post template syntax errors.
"""
pre_render function
jinja_md hook for markata to render your markdown post as a jinja template.
The post itself is exposed as post, and the markata instance is exposed
as markata.
pre_render source
def pre_render(markata: "Markata") -> None:
"""
jinja_md hook for markata to render your markdown post as a jinja template.
The post itself is exposed as `post`, and the markata instance is exposed
as `markata`.
"""
config = markata.get_plugin_config("jinja_md")
ignore_spec = pathspec.PathSpec.from_lines("gitwildmatch", config.get("ignore", []))
# for article in markata.iter_articles(description="jinja_md"):
jinja_env = jinja2.Environment(
extensions=[IncludeRawExtension, *register_jinja_extensions(config)],
)
_full_config = copy.deepcopy(markata.config)
for article in markata.articles:
if article.get("jinja", True) and not ignore_spec.match_file(article["path"]):
try:
article.content = jinja_env.from_string(article.content).render(
__version__=__version__,
markata=markata,
config=always_merger.merge(
_full_config,
copy.deepcopy(
article.get(
"config_overrides",
{},
),
),
),
**article,
)
# prevent double rendering
article["jinja"] = False
except TemplateSyntaxError as e:
errorline = article.content.split("\n")[e.lineno - 1]
msg = f"""
Error while processing post {article['path']}
{errorline}
"""
# markata.run_one_lifecycle("error")
raise PostTemplateSyntaxError(msg, lineno=e.lineno)
except UndefinedError as e:
...
parse method
None
parse source
def parse(self, parser):
line_number = next(parser.stream).lineno
file = [parser.parse_expression()]
return nodes.CallBlock(
self.call_method("_read_file", file), [], [], ""
).set_lineno(line_number)
_read_file method
None
_read_file source
def _read_file(self, file, caller):
return Path(file).read_text()
_fail_with_undefined_error method
None
_fail_with_undefined_error source
def _fail_with_undefined_error(self, *args, **kwargs):
return ""