Custom Filter for Jinja Renderer
Hello @radeklat , thanks for the last update to expose classes. So far, I am enjoying my experience in auto generating configurations for my apps :D.
Currently I am contemplating the solution for one of my use cases. I want to display the class title and render each classes sorted by their titles. I displayed the title by using the following macro:
{% macro class_title(class) -%}
{% if class.Config|attr('title') -%}
{{ class.Config.title }}
{%- else -%}
{{ class.__name__ }}
{%- endif %}
{%- endmacro %}
However, I reached a conundrum while trying to sort the classes by their title. After some tinkering, I figured out a way to achieve this is to add custom filters. So I modified the main.py (for testing purpose):
class_title = env.globals["class_title"] = lambda x: x.Config.title if hasattr(getattr(x, 'Config', None), 'title') else x.__name__
env.filters["sort_title"] = lambda x: list(sorted(x, key=class_title))
And I use them in the following way:
{% for class in classes|sort_title %}
{{ heading(2) }} {{ class_title(class) }}
{% endfor %}
I am wondering what are your thoughts on this and if it is worth supporting custom filters without having to modify the main.py. I will gladly write a pull request if you think that this is a good idea/feature to support.
Hi Suhendi. Custom filters certainly make sense, even outside of this use case. It could be perhaps implemented as new command line option, pointing to a Python file that contains custom filter functions? And use the function names as the filter names.
For example:
settings-doc ... --jinja-filters filters.py
where filters.py could look like:
from typing import Type, List, Iterable
def class_title(cls: Type) -> str:
return cls.Config.title if hasattr(getattr(cls, 'Config', None), 'title') else cls.__name__
def sort_title(iterable: Iterable) -> List:
return list(sorted(iterable, key=class_title))
Thanks for your feedback. I will get started with a draft of sort.
On the topic of expanding the jinja functionality, I am thinking that maybe we can instead create some sort of hooks that passes the jinja Environment to be modified freely.
Something along the line of the following:
settings-doc ... --hooks settings_doc_hooks.py
where settings_doc_hooks.py could contain the hook (if not multiple hooks):
from typing import Type, List, Iterable
from jinja2 import Environment
def class_title(cls: Type) -> str:
return cls.Config.title if hasattr(getattr(cls, 'Config', None), 'title') else cls.__name__
def sort_title(iterable: Iterable) -> List:
return list(sorted(iterable, key=class_title))
def settings_doc_initialize_environment(env: Environment) -> None:
env.globals["class_title"] = class_title
env.filters["sort_title"] = sort_title
This way we can extend some of the functionality such as creating a hook for preprocessing the classes: List[Type[BaseSettings]] before passing it to the renderer
That's also a good idea :+1: It is likely more attributes of the Environment will need customization. So why not to expose it entirely.