hexdoc.jinja
1__all__ = [ 2 "IncludeRawExtension", 3 "hexdoc_item", 4 "hexdoc_localize", 5 "hexdoc_smart_var", 6 "hexdoc_texture", 7 "hexdoc_wrap", 8] 9 10from .extensions import IncludeRawExtension 11from .filters import ( 12 hexdoc_item, 13 hexdoc_localize, 14 hexdoc_smart_var, 15 hexdoc_texture, 16 hexdoc_wrap, 17)
9class IncludeRawExtension(Extension): 10 tags = {"include_raw"} 11 12 def parse(self, parser: Parser) -> nodes.Node | list[nodes.Node]: 13 lineno = parser.stream.expect("name:include_raw").lineno 14 template = parser.parse_expression() 15 result = self.call_method("_render", [template], lineno=lineno) 16 return nodes.Output([result], lineno=lineno) 17 18 def _render(self, filename: str) -> Markup: 19 assert self.environment.loader is not None 20 source = self.environment.loader.get_source(self.environment, filename) 21 return Markup(source[0])
Extensions can be used to add extra functionality to the Jinja template
system at the parser level. Custom extensions are bound to an environment
but may not store environment specific data on self
. The reason for
this is that an extension can be bound to another environment (for
overlays) by creating a copy and reassigning the environment
attribute.
As extensions are created by the environment they cannot accept any
arguments for configuration. One may want to work around that by using
a factory function, but that is not possible as extensions are identified
by their import name. The correct way to configure the extension is
storing the configuration values on the environment. Because this way the
environment ends up acting as central configuration storage the
attributes may clash which is why extensions have to ensure that the names
they choose for configuration are not too generic. prefix
for example
is a terrible name, fragment_cache_prefix
on the other hand is a good
name as includes the name of the extension (fragment cache).
12 def parse(self, parser: Parser) -> nodes.Node | list[nodes.Node]: 13 lineno = parser.stream.expect("name:include_raw").lineno 14 template = parser.parse_expression() 15 result = self.call_method("_render", [template], lineno=lineno) 16 return nodes.Output([result], lineno=lineno)
If any of the tags
matched this method is called with the
parser as first argument. The token the parser stream is pointing at
is the name token that matched. This method has to return one or a
list of multiple nodes.
55@make_jinja_exceptions_suck_a_bit_less 56def hexdoc_localize( 57 key: str, 58 *, 59 do_format: bool, 60 props: Properties, 61 book_id: ResourceLocation, 62 i18n: I18n, 63 macros: dict[str, str], 64 pm: PluginManager, 65): 66 # get the localized value from i18n 67 localized = i18n.localize(key) 68 69 if not do_format: 70 return Markup(localized.value) 71 72 # construct a FormatTree from the localized value (to allow using patchi styles) 73 formatted = FormatTree.format( 74 localized.value, 75 book_id=book_id, 76 i18n=i18n, 77 macros=macros, 78 is_0_black=props.is_0_black, 79 pm=pm, 80 link_overrides=props.link_overrides, 81 ) 82 return formatted
109@pass_context 110@make_jinja_exceptions_suck_a_bit_less 111def hexdoc_smart_var(context: Context, value: Any): 112 """Smart template argument filter. 113 114 If `value` is of the form `{"variable": str(ref)}`, returns the value of the 115 template variable called `ref`. 116 117 Otherwise, returns `value` unchanged. 118 """ 119 120 match value: 121 case {**items} if len(items) != 1: 122 return value 123 case {"variable": str(ref)}: 124 return context.resolve(ref) 125 case _: 126 return value
Smart template argument filter.
If value
is of the form {"variable": str(ref)}
, returns the value of the
template variable called ref
.
Otherwise, returns value
unchanged.