+ self.headers: list[tuple[str, str]] = []
+ self._render_mode = 'html'
+ self._jinja = JinjaEnv(loader=JinjaFSLoader(TEMPLATES_DIR))
+
+ def set_json_mode(self) -> None:
+ """Make server send JSON instead of HTML responses."""
+ self._render_mode = 'json'
+ self.headers += [('Content-Type', 'application/json')]
+
+ @staticmethod
+ def ctx_to_json(ctx: dict[str, object]) -> str:
+ """Render ctx into JSON string."""
+ def walk_ctx(node: object) -> Any:
+ if hasattr(node, 'as_dict_into_reference'):
+ if hasattr(node, 'id_') and node.id_ is not None:
+ return node.as_dict_into_reference(library)
+ if hasattr(node, 'as_dict'):
+ return node.as_dict
+ if isinstance(node, (list, tuple)):
+ return [walk_ctx(x) for x in node]
+ if isinstance(node, HandledException):
+ return str(node)
+ return node
+ library: dict[str, dict[str | int, object]] = {}
+ for k, v in ctx.items():
+ ctx[k] = walk_ctx(v)
+ ctx['_library'] = library
+ return json_dumps(ctx)
+
+ def render(self, ctx: dict[str, object], tmpl_name: str = '') -> str:
+ """Render ctx according to self._render_mode.."""
+ tmpl_name = f'{tmpl_name}.{self._render_mode}'
+ if 'html' == self._render_mode:
+ template = self._jinja.get_template(tmpl_name)
+ return template.render(ctx)
+ return self.__class__.ctx_to_json(ctx)