settings-doc icon indicating copy to clipboard operation
settings-doc copied to clipboard

Custom Filter for Jinja Renderer

Open suhendiandigo opened this issue 3 years ago • 3 comments

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.

suhendiandigo avatar Jan 26 '22 14:01 suhendiandigo

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))

radeklat avatar Jan 28 '22 11:01 radeklat

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

suhendiandigo avatar Jan 28 '22 14:01 suhendiandigo

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.

radeklat avatar Jan 28 '22 15:01 radeklat