diff libervia/backend/tools/common/template.py @ 4270:0d7bb4df2343

Reformatted code base using black.
author Goffi <goffi@goffi.org>
date Wed, 19 Jun 2024 18:44:57 +0200
parents 81faa85c9cfa
children
line wrap: on
line diff
--- a/libervia/backend/tools/common/template.py	Tue Jun 18 12:06:45 2024 +0200
+++ b/libervia/backend/tools/common/template.py	Wed Jun 19 18:44:57 2024 +0200
@@ -72,7 +72,6 @@
     )
 
 
-
 HTML_EXT = ("html", "xhtml")
 RE_ATTR_ESCAPE = re.compile(r"[^a-z_-]")
 SITE_RESERVED_NAMES = ("sat",)
@@ -81,11 +80,12 @@
 BROWSER_DIR = "_browser"
 BROWSER_META_FILE = "browser_meta.json"
 
-TemplateData = namedtuple("TemplateData", ['site', 'theme', 'path'])
+TemplateData = namedtuple("TemplateData", ["site", "theme", "path"])
 
 
 class TemplateLoader(jinja2.BaseLoader):
     """A template loader which handle site, theme and absolute paths"""
+
     # TODO: list_templates should be implemented
 
     def __init__(self, sites_paths, sites_themes, trusted=False):
@@ -126,14 +126,14 @@
             except IndexError:
                 raise ValueError("incorrect site/theme in template")
             theme_data = template[1:theme_end]
-            theme_splitted = theme_data.split('/')
+            theme_splitted = theme_data.split("/")
             if len(theme_splitted) == 1:
                 site, theme = "", theme_splitted[0]
             elif len(theme_splitted) == 2:
                 site, theme = theme_splitted
             else:
                 raise ValueError("incorrect site/theme in template")
-            template_path = template[theme_end+1:]
+            template_path = template[theme_end + 1 :]
             if not template_path or template_path.startswith("/"):
                 raise ValueError("incorrect template path")
         elif template.startswith("/"):
@@ -152,26 +152,32 @@
             if not site:
                 site = ""
             elif site in SITE_RESERVED_NAMES:
-                raise ValueError(_("{site} can't be used as site name, "
-                                   "it's reserved.").format(site=site))
+                raise ValueError(
+                    _("{site} can't be used as site name, " "it's reserved.").format(
+                        site=site
+                    )
+                )
 
         if theme is not None:
             theme = theme.strip()
             if not theme:
                 theme = C.TEMPLATE_THEME_DEFAULT
             if RE_TPL_RESERVED_CHARS.search(theme):
-                raise ValueError(_("{theme} contain forbidden char. Following chars "
-                                   "are forbidden: {reserved}").format(
-                                   theme=theme, reserved=TPL_RESERVED_CHARS))
+                raise ValueError(
+                    _(
+                        "{theme} contain forbidden char. Following chars "
+                        "are forbidden: {reserved}"
+                    ).format(theme=theme, reserved=TPL_RESERVED_CHARS)
+                )
 
         return TemplateData(site, theme, template_path)
 
     @staticmethod
     def get_sites_and_themes(
-            site: str,
-            theme: str,
-            settings: Optional[dict] = None,
-        ) -> List[Tuple[str, str]]:
+        site: str,
+        theme: str,
+        settings: Optional[dict] = None,
+    ) -> List[Tuple[str, str]]:
         """Get sites and themes to check for template/file
 
         Will add default theme and default site in search list when suitable. Settings'
@@ -208,22 +214,24 @@
         """
         if site is None:
             raise exceptions.InternalError(
-                "_get_template_f must not be used with absolute path")
-        settings = self.sites_themes[site][theme]['settings']
+                "_get_template_f must not be used with absolute path"
+            )
+        settings = self.sites_themes[site][theme]["settings"]
         for site_to_check, theme_to_check in self.get_sites_and_themes(
-                site, theme, settings):
+            site, theme, settings
+        ):
             try:
                 base_path = self.sites_paths[site_to_check]
             except KeyError:
-                log.warning(_("Unregistered site requested: {site_to_check}").format(
-                    site_to_check=site_to_check))
+                log.warning(
+                    _("Unregistered site requested: {site_to_check}").format(
+                        site_to_check=site_to_check
+                    )
+                )
             filepath = os.path.join(
-                base_path,
-                C.TEMPLATE_TPL_DIR,
-                theme_to_check,
-                *path_elts
+                base_path, C.TEMPLATE_TPL_DIR, theme_to_check, *path_elts
             )
-            f = utils.open_if_exists(filepath, 'r')
+            f = utils.open_if_exists(filepath, "r")
             if f is not None:
                 return f, filepath
         return None, None
@@ -241,14 +249,19 @@
         if site is None:
             # we have an abolute template
             if theme is not None:
-                raise exceptions.InternalError("We can't have a theme with absolute "
-                                               "template.")
+                raise exceptions.InternalError(
+                    "We can't have a theme with absolute " "template."
+                )
             if not self.trusted:
-                log.error(_("Absolute template used while unsecure is disabled, hack "
-                            "attempt? Template: {template}").format(template=template))
+                log.error(
+                    _(
+                        "Absolute template used while unsecure is disabled, hack "
+                        "attempt? Template: {template}"
+                    ).format(template=template)
+                )
                 raise exceptions.PermissionError("absolute template is not allowed")
             filepath = template_path
-            f = utils.open_if_exists(filepath, 'r')
+            f = utils.open_if_exists(filepath, "r")
         else:
             # relative path, we have to deal with site and theme
             assert theme and template_path
@@ -257,12 +270,14 @@
             f, filepath = self._get_template_f(site, theme, path_elts)
 
         if f is None:
-            if (site is not None and path_elts[0] == "error"
-                and os.path.splitext(template_path)[1][1:] in HTML_EXT):
+            if (
+                site is not None
+                and path_elts[0] == "error"
+                and os.path.splitext(template_path)[1][1:] in HTML_EXT
+            ):
                 # if an HTML error is requested but doesn't exist, we try again
                 # with base error.
-                f, filepath = self._get_template_f(
-                    site, theme, ("error", "base.html"))
+                f, filepath = self._get_template_f(site, theme, ("error", "base.html"))
                 if f is None:
                     raise exceptions.InternalError("error/base.html should exist")
             else:
@@ -335,8 +350,9 @@
         for library, attribute in self.scripts:
             library_path = self.renderer.get_static_path(self.template_data, library)
             if library_path is None:
-                log.warning(_("Can't find {libary} javascript library").format(
-                    library=library))
+                log.warning(
+                    _("Can't find {libary} javascript library").format(library=library)
+                )
                 continue
             path = self.renderer.get_front_url(library_path)
             scripts.append(tpl.format(src=quoteattr(path), attribute=attribute))
@@ -346,7 +362,7 @@
 class Environment(jinja2.Environment):
 
     def get_template(self, name, parent=None, globals=None):
-        if name[0] not in ('/', '('):
+        if name[0] not in ("/", "("):
             # if name is not an absolute path or a full template name (this happen on
             # extend or import during rendering), we convert it to a full template name.
             # This is needed to handle cache correctly when a base template is overriden.
@@ -355,7 +371,8 @@
             name = "({site}/{theme}){template}".format(
                 site=self._template_data.site,
                 theme=self._template_data.theme,
-                template=name)
+                template=name,
+            )
 
         return super(Environment, self).get_template(name, parent, globals)
 
@@ -380,8 +397,7 @@
         self.sites_paths = {
             "": os.path.dirname(sat_templates.__file__),
         }
-        self.sites_themes = {
-        }
+        self.sites_themes = {}
         conf = config.parse_main_conf()
         public_sites = config.config_get(conf, None, "sites_path_public_dict", {})
         sites_data = [public_sites]
@@ -392,14 +408,21 @@
             normalised = {}
             for name, path in sites.items():
                 if RE_TPL_RESERVED_CHARS.search(name):
-                    log.warning(_("Can't add \"{name}\" site, it contains forbidden "
-                                  "characters. Forbidden characters are {forbidden}.")
-                                .format(name=name, forbidden=TPL_RESERVED_CHARS))
+                    log.warning(
+                        _(
+                            'Can\'t add "{name}" site, it contains forbidden '
+                            "characters. Forbidden characters are {forbidden}."
+                        ).format(name=name, forbidden=TPL_RESERVED_CHARS)
+                    )
                     continue
                 path = os.path.expanduser(os.path.normpath(path))
                 if not path or not path.startswith("/"):
-                    log.warning(_("Can't add \"{name}\" site, it should map to an "
-                                  "absolute path").format(name=name))
+                    log.warning(
+                        _(
+                            'Can\'t add "{name}" site, it should map to an '
+                            "absolute path"
+                        ).format(name=name)
+                    )
                     continue
                 normalised[name] = path
             self.sites_paths.update(normalised)
@@ -411,20 +434,24 @@
                     continue
                 log.debug(f"theme found for {site or 'default site'}: {p.name}")
                 theme_data = self.sites_themes.setdefault(site, {})[p.name] = {
-                    'path': p,
-                    'settings': {}}
+                    "path": p,
+                    "settings": {},
+                }
                 theme_settings = p / "settings.json"
                 if theme_settings.is_file:
                     try:
                         with theme_settings.open() as f:
                             settings = json.load(f)
                     except Exception as e:
-                        log.warning(_(
-                            "Can't load theme settings at {path}: {e}").format(
-                            path=theme_settings, e=e))
+                        log.warning(
+                            _("Can't load theme settings at {path}: {e}").format(
+                                path=theme_settings, e=e
+                            )
+                        )
                     else:
                         log.debug(
-                            f"found settings for theme {p.name!r} at {theme_settings}")
+                            f"found settings for theme {p.name!r} at {theme_settings}"
+                        )
                         fallback = settings.get("fallback")
                         if fallback is None:
                             settings["fallback"] = []
@@ -433,17 +460,17 @@
                         elif not isinstance(fallback, list):
                             raise ValueError(
                                 'incorrect type for "fallback" in settings '
-                                f'({type(fallback)}) at {theme_settings}: {fallback}'
+                                f"({type(fallback)}) at {theme_settings}: {fallback}"
                             )
-                        theme_data['settings'] = settings
+                        theme_data["settings"] = settings
                 browser_path = p / BROWSER_DIR
                 if browser_path.is_dir():
-                    theme_data['browser_path'] = browser_path
+                    theme_data["browser_path"] = browser_path
                 browser_meta_path = browser_path / BROWSER_META_FILE
                 if browser_meta_path.is_file():
                     try:
                         with browser_meta_path.open() as f:
-                            theme_data['browser_meta'] = json.load(f)
+                            theme_data["browser_meta"] = json.load(f)
                     except Exception as e:
                         log.error(
                             f"Can't parse browser metadata at {browser_meta_path}: {e}"
@@ -454,7 +481,7 @@
             loader=TemplateLoader(
                 sites_paths=self.sites_paths,
                 sites_themes=self.sites_themes,
-                trusted=trusted
+                trusted=trusted,
             ),
             autoescape=jinja2.select_autoescape(["html", "xhtml", "xml"]),
             trim_blocks=True,
@@ -482,8 +509,9 @@
         self.env.filters["adv_format"] = self._adv_format
         self.env.filters["dict_ext"] = self._dict_ext
         self.env.filters["highlight"] = self.highlight
-        self.env.filters["front_url"] = (self._front_url if front_url_filter is None
-                                         else front_url_filter)
+        self.env.filters["front_url"] = (
+            self._front_url if front_url_filter is None else front_url_filter
+        )
         self.env.filters["media_type_main"] = self.media_type_main
         self.env.filters["media_type_sub"] = self.media_type_sub
         # custom tests
@@ -495,7 +523,7 @@
         self.env.policies["json.dumps_kwargs"] = {
             "sort_keys": True,
             # if object can't be serialised, we use None
-            "default": lambda o: o.to_json() if hasattr(o, "to_json") else None
+            "default": lambda o: o.to_json() if hasattr(o, "to_json") else None,
         }
 
     def get_front_url(self, template_data, path=None):
@@ -505,15 +533,16 @@
         @param path(unicode, None): relative path of file to get,
             if set, will remplate template_data.path
         """
-        return self.env.filters["front_url"]({"template_data": template_data},
-                                path or template_data.path)
+        return self.env.filters["front_url"](
+            {"template_data": template_data}, path or template_data.path
+        )
 
     def install_translations(self):
         # TODO: support multi translation
         #       for now, only translations in sat_templates are handled
         self.translations = {}
         for site_key, site_path in self.sites_paths.items():
-            site_prefix = "[{}] ".format(site_key) if site_key else ''
+            site_prefix = "[{}] ".format(site_key) if site_key else ""
             i18n_dir = os.path.join(site_path, "i18n")
             for lang_dir in os.listdir(i18n_dir):
                 lang_path = os.path.join(i18n_dir, lang_dir)
@@ -532,14 +561,21 @@
                 except EnvironmentError:
                     log.error(
                         _("Can't find template translation at {path}").format(
-                            path=po_path))
+                            path=po_path
+                        )
+                    )
                 except UnknownLocaleError as e:
-                    log.error(_("{site}Invalid locale name: {msg}").format(
-                        site=site_prefix, msg=e))
+                    log.error(
+                        _("{site}Invalid locale name: {msg}").format(
+                            site=site_prefix, msg=e
+                        )
+                    )
                 else:
-                    log.info(_("{site}loaded {lang} templates translations").format(
-                        site = site_prefix,
-                        lang=lang_dir))
+                    log.info(
+                        _("{site}loaded {lang} templates translations").format(
+                            site=site_prefix, lang=lang_dir
+                        )
+                    )
 
         default_locale = Locale.parse(self._locale_str)
         if default_locale not in self.translations:
@@ -550,9 +586,9 @@
         self.env.install_null_translations(True)
         # we generate a tuple of locales ordered by display name that templates can access
         # through the "locales" variable
-        self.locales = tuple(sorted(list(self.translations.keys()),
-                                    key=lambda l: l.language_name.lower()))
-
+        self.locales = tuple(
+            sorted(list(self.translations.keys()), key=lambda l: l.language_name.lower())
+        )
 
     def set_locale(self, locale_str):
         """set current locale
@@ -601,7 +637,7 @@
         site, theme, __ = self.env.loader.parse_template(template)
         if site is None:
             # absolute template
-            return  "", os.path.dirname(template)
+            return "", os.path.dirname(template)
         try:
             site_root_dir = self.sites_paths[site]
         except KeyError:
@@ -615,11 +651,8 @@
             raise exceptions.NotFound(f"no theme found for {site_name}")
 
     def get_static_path(
-            self,
-            template_data: TemplateData,
-            filename: str,
-            settings: Optional[dict]=None
-        ) -> Optional[TemplateData]:
+        self, template_data: TemplateData, filename: str, settings: Optional[dict] = None
+    ) -> Optional[TemplateData]:
         """Retrieve path of a static file if it exists with current theme or default
 
         File will be looked at <site_root_dir>/<theme_dir>/<static_dir>/filename,
@@ -637,10 +670,10 @@
         """
         if template_data.site is None:
             # we have an absolue path
-            if (not template_data.theme is None
-                or not template_data.path.startswith('/')):
+            if not template_data.theme is None or not template_data.path.startswith("/"):
                 raise exceptions.InternalError(
-                    "invalid template data, was expecting absolute URL")
+                    "invalid template data, was expecting absolute URL"
+                )
             static_dir = os.path.dirname(template_data.path)
             file_path = os.path.join(static_dir, filename)
             if os.path.exists(file_path):
@@ -648,28 +681,28 @@
             else:
                 return None
 
-        sites_and_themes = TemplateLoader.get_sites_and_themes(template_data.site,
-                                                            template_data.theme,
-                                                            settings)
+        sites_and_themes = TemplateLoader.get_sites_and_themes(
+            template_data.site, template_data.theme, settings
+        )
         for site, theme in sites_and_themes:
             site_root_dir = self.sites_paths[site]
             relative_path = os.path.join(C.TEMPLATE_STATIC_DIR, filename)
-            absolute_path = os.path.join(site_root_dir, C.TEMPLATE_TPL_DIR,
-                                         theme, relative_path)
+            absolute_path = os.path.join(
+                site_root_dir, C.TEMPLATE_TPL_DIR, theme, relative_path
+            )
             if os.path.exists(absolute_path):
                 return TemplateData(site=site, theme=theme, path=relative_path)
 
         return None
 
     def _append_css_paths(
-            self,
-            template_data: TemplateData,
-            css_files: list,
-            css_files_noscript: list,
-            name_root: str,
-            settings: dict
-
-        ) -> None:
+        self,
+        template_data: TemplateData,
+        css_files: list,
+        css_files_noscript: list,
+        name_root: str,
+        settings: dict,
+    ) -> None:
         """Append found css to css_files and css_files_noscript
 
         @param css_files: list to fill of relative path to found css file
@@ -681,8 +714,7 @@
         if css_path is not None:
             css_files.append(self.get_front_url(css_path))
             noscript_name = name_root + "_noscript.css"
-            noscript_path = self.get_static_path(
-                template_data, noscript_name, settings)
+            noscript_path = self.get_static_path(template_data, noscript_name, settings)
             if noscript_path is not None:
                 css_files_noscript.append(self.get_front_url(noscript_path))
 
@@ -718,27 +750,29 @@
         # TODO: some caching would be nice
         css_files = []
         css_files_noscript = []
-        path_elems = template_data.path.split('/')
+        path_elems = template_data.path.split("/")
         path_elems[-1] = os.path.splitext(path_elems[-1])[0]
         site = template_data.site
         if site is None:
             # absolute path
             settings = {}
         else:
-            settings = self.sites_themes[site][template_data.theme]['settings']
+            settings = self.sites_themes[site][template_data.theme]["settings"]
 
-        css_path = self.get_static_path(template_data, 'fonts.css', settings)
+        css_path = self.get_static_path(template_data, "fonts.css", settings)
         if css_path is not None:
             css_files.append(self.get_front_url(css_path))
 
-        for name_root in ('styles', 'styles_extra', 'highlight'):
+        for name_root in ("styles", "styles_extra", "highlight"):
             self._append_css_paths(
-                template_data, css_files, css_files_noscript, name_root, settings)
+                template_data, css_files, css_files_noscript, name_root, settings
+            )
 
         for idx in range(len(path_elems)):
-            name_root = "_".join(path_elems[:idx+1])
+            name_root = "_".join(path_elems[: idx + 1])
             self._append_css_paths(
-                template_data, css_files, css_files_noscript, name_root, settings)
+                template_data, css_files, css_files_noscript, name_root, settings
+            )
 
         return css_files, css_files_noscript
 
@@ -750,17 +784,18 @@
 
         This default method return absolute full path
         """
-        template_data = ctx['template_data']
+        template_data = ctx["template_data"]
         if template_data.site is None:
             assert template_data.theme is None
             assert template_data.path.startswith("/")
             return os.path.join(os.path.dirname(template_data.path, relative_url))
 
         site_root_dir = self.sites_paths[template_data.site]
-        return os.path.join(site_root_dir, C.TEMPLATE_TPL_DIR, template_data.theme,
-                            relative_url)
+        return os.path.join(
+            site_root_dir, C.TEMPLATE_TPL_DIR, template_data.theme, relative_url
+        )
 
-    def _bare_jid(self, full_jid: str|jid.JID) -> str:
+    def _bare_jid(self, full_jid: str | jid.JID) -> str:
         """Return the bare JID"""
         return str(jid.JID(str(full_jid)).bare)
 
@@ -784,16 +819,21 @@
         auto_limit: int = 7,
         auto_old_fmt: str = "short",
         auto_new_fmt: str = "relative",
-        tz_name: Optional[str] = None
+        tz_name: Optional[str] = None,
     ) -> str:
         if is_undefined(fmt):
             fmt = "short"
 
         try:
             return date_utils.date_fmt(
-                timestamp, fmt, date_only, auto_limit, auto_old_fmt,
-                auto_new_fmt, locale_str = self._locale_str,
-                tz_info=tz_name or date_utils.TZ_UTC
+                timestamp,
+                fmt,
+                date_only,
+                auto_limit,
+                auto_old_fmt,
+                auto_new_fmt,
+                locale_str=self._locale_str,
+                tz_info=tz_name or date_utils.TZ_UTC,
             )
         except Exception as e:
             log.warning(_("Can't parse date: {msg}").format(msg=e))
@@ -986,30 +1026,39 @@
             '<svg class="svg-icon{cls}"{extra_attrs} xmlns="http://www.w3.org/2000/svg" '
             'viewBox="0 0 100 100">\n'
             '    <use href="#{name}"/>'
-            '</svg>\n'.format(
+            "</svg>\n".format(
                 name=name,
                 cls=(" " + cls) if cls else "",
-                extra_attrs=" " + extra_attrs if extra_attrs else ""
+                extra_attrs=" " + extra_attrs if extra_attrs else "",
             )
         )
 
     def _icon_from_client(self, client):
         """Get icon name to represent a disco client"""
         if client is None:
-            return 'desktop'
-        elif 'pc' in client:
-            return 'desktop'
-        elif 'phone' in client:
-            return 'mobile'
-        elif 'web' in client:
-            return 'globe'
-        elif 'console' in client:
-            return 'terminal'
+            return "desktop"
+        elif "pc" in client:
+            return "desktop"
+        elif "phone" in client:
+            return "mobile"
+        elif "web" in client:
+            return "globe"
+        elif "console" in client:
+            return "terminal"
         else:
-            return 'desktop'
+            return "desktop"
 
-    def render(self, template, site=None, theme=None, locale=C.DEFAULT_LOCALE,
-               media_path="", css_files=None, css_inline=False, **kwargs):
+    def render(
+        self,
+        template,
+        site=None,
+        theme=None,
+        locale=C.DEFAULT_LOCALE,
+        media_path="",
+        css_files=None,
+        css_inline=False,
+        **kwargs,
+    ):
         """Render a template
 
         @param template(unicode): template to render (e.g. blog/articles.html)
@@ -1031,7 +1080,7 @@
         if site is not None or theme is not None:
             # user wants to set site and/or theme, so we add it to the template path
             if site is None:
-                site = ''
+                site = ""
             if theme is None:
                 theme = C.TEMPLATE_THEME_DEFAULT
             if template[0] == "(":
@@ -1042,7 +1091,8 @@
 
             template_data = TemplateData(site, theme, template)
             template = "({site}/{theme}){template}".format(
-                site=site, theme=theme, template=template)
+                site=site, theme=theme, template=template
+            )
         else:
             template_data = self.env.loader.parse_template(template)
 
@@ -1066,8 +1116,7 @@
 
         if css_inline:
             css_contents = []
-            for files, suffix in ((css_files, ""),
-                                  (css_files_noscript, "_noscript")):
+            for files, suffix in ((css_files, ""), (css_files_noscript, "_noscript")):
                 site_root_dir = self.sites_paths[template_data.site]
                 for css_file in files:
                     css_file_path = os.path.join(site_root_dir, css_file)
@@ -1090,7 +1139,7 @@
             locales=self.locales,
             gidx=Indexer(),
             script=scripts_handler,
-            **kwargs
+            **kwargs,
         )
         self.env._template_data = None
         return rendered