Source code for nunavut.lang.html

#
# Copyright (C) OpenCyphal Development Team  <opencyphal.org>
# Copyright Amazon.com Inc. or its affiliates.
# SPDX-License-Identifier: MIT
#
"""
    Filters for generating docs. All filters in this
    module will be available in the template's global namespace as ``ln.html``.
"""

import html
import logging
import re
import typing

import pydsdl

import nunavut
from nunavut._templates import template_volatile_filter
from nunavut.jinja.jinja2 import TemplateAssertionError
from nunavut.lang._common import UniqueNameGenerator

logger = logging.getLogger(__name__)


[docs] def filter_extent(instance: pydsdl.Any) -> int: """ Filter that returns the dsdl extend property of a given type. """ try: return instance.extent or 0 except TypeError as e: raise TemplateAssertionError(e) from None
[docs] def filter_max_bit_length(instance: pydsdl.Any) -> int: """ Filter that returns the dsdl max bit length property of a given type. """ try: return instance.bit_length_set.max or 0 except TypeError as e: raise TemplateAssertionError(e) from None
[docs] def filter_tag_id(instance: pydsdl.Any) -> str: """ Emit a tag id for a given type. """ if isinstance(instance, pydsdl.ArrayType): return "{}_array".format(str(instance.element_type).replace(".", "_").replace(" ", "_")) else: return "{}_{}_{}".format( instance.full_name.replace(".", "_"), instance.version[0], instance.version[1], )
[docs] def filter_url_from_type(instance: pydsdl.Any) -> str: """ Emit a path to the documentation for a given type. """ root_ns = instance.root_namespace tag_id = "{}_{}_{}".format(instance.full_name.replace(".", "_"), instance.version[0], instance.version[1]) return "../{}/#{}".format(root_ns, tag_id)
[docs] @template_volatile_filter def filter_make_unique(_: typing.Any, base_token: str) -> str: """ Filter that takes a base token and forms a name that is very likely to be unique within the template the filter is invoked. .. IMPORTANT:: The exact tokens generated may change between major or minor versions of this library. The only guarantee provided is that the tokens will be stable for the same version of this library given the same input. Also note that name uniqueness is only likely within a given template. Between templates there is no guarantee of uniqueness and, since this library does not lex generated source, there is no guarantee that the generated name does not conflict with a name generated by another means. .. invisible-code-block: python from nunavut.lang.html import filter_make_unique from nunavut.lang._common import UniqueNameGenerator .. code-block:: python # Given template = '{{ "foo" | make_unique }},{{ "Foo" | make_unique }},' template += '{{ "fOO" | make_unique }}' # then rendered = 'foo0,foo1,fOO0' .. invisible-code-block: python UniqueNameGenerator.reset() jinja_filter_tester(filter_make_unique, template, rendered, 'html') .. code-block:: python # Given template = '{{ "coffee > tea" | make_unique }}' # then rendered = 'coffee &gt; tea0' .. invisible-code-block: python UniqueNameGenerator.reset() jinja_filter_tester(filter_make_unique, template, rendered, 'html') :param str base_token: A token to include in the base name. :return: A name that is likely to be unique within the file generated by the current template. """ if len(base_token) > 0: adj_base_token = base_token[0:1].lower() + base_token[1:] else: adj_base_token = base_token escaped_base_token = html.escape(adj_base_token) return UniqueNameGenerator.get_instance()("html", escaped_base_token, "", "")
[docs] def filter_namespace_doc(ns: nunavut.Namespace) -> str: """ Generate HTML documentation for a namespace. """ result = "" for t, _ in ns.get_nested_types(): if t.short_name == "_": result = t.doc break return result
[docs] def filter_display_type(instance: pydsdl.Any) -> str: """ Deprecated. Don't use this filter. Needs refactoring. """ # TODO: this whole thing needs to be in the template. if isinstance(instance, pydsdl.FixedLengthArrayType): capacity = '<span style="color: green">[{}]</span>'.format(instance.capacity) return filter_display_type(instance.element_type) + capacity elif isinstance(instance, pydsdl.VariableLengthArrayType): capacity = '<span style="color: green">[<={}]</span>'.format(instance.capacity) return filter_display_type(instance.element_type) + capacity elif isinstance(instance, pydsdl.PaddingField): return '<span style="color: gray">{}</span>'.format(instance) elif isinstance(instance, pydsdl.Field): return "{} {}".format(filter_display_type(instance.data_type), instance.name) elif isinstance(instance, pydsdl.Constant): name = '<span style="color: darkmagenta">{}</span>'.format(instance.name) value = '<span style="color: darkcyan">{}</span>'.format(instance.value) return "{} {} = {}".format(filter_display_type(instance.data_type), name, value) elif isinstance(instance, pydsdl.PrimitiveType): if instance.cast_mode == instance.cast_mode.SATURATED: is_saturated = '<span style="color: gray">saturated</span> ' else: is_saturated = '<span style="color: orange">truncated</span> ' type_name = '<span style="color: green">{}</span>'.format(str(instance).split()[-1]) return "{}{}".format(is_saturated, type_name) else: return str(instance)
def _natural_sort(instance: typing.List[pydsdl.Any], key: typing.Callable = lambda s: s) -> typing.List[pydsdl.Any]: def natural_sort_key(s: str, _nsre: typing.Pattern = re.compile("([0-9]+)")) -> typing.List[pydsdl.Any]: _key = key(s) return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(_key)] return sorted(instance, key=natural_sort_key)
[docs] def filter_natural_sort_namespace(instance: typing.List[pydsdl.Any]) -> typing.List[pydsdl.Any]: """ Namespaces come in plain lists; sort by name only. """ return _natural_sort(instance, key=lambda s: s.full_name)
[docs] def filter_natural_sort_type(instance: pydsdl.Any) -> typing.List[pydsdl.Any]: """ Types come in tuples (type, path). Sort by type name. """ return _natural_sort(instance, key=lambda s: s[0].full_name)