Mercurial > libervia-backend
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