Source code for pantra.cached.renderer

from __future__ import annotations

import typing

from pantra.components.render.renderer_base import RendererBase, ForLoopType
from pantra.components.render.renderer_html import RendererHTML
from pantra.common import typename
from pantra.components.template import HTMLTemplate
from pantra.settings import config, logger

if typing.TYPE_CHECKING:
    from typing import *
    from types import CodeType
    from pantra.session import Session
    from pantra.components.context import Condition, LoopNode, SetNode
    from pantra.components.render.render_node import RenderNode

[docs] class RendererCached(RendererBase): """Renderer from cached component classes""" templates: ClassVar[dict[str, CodeType]] = {} __slots__ = () @classmethod def collect_template(cls, name: str, session: Optional[Session] = None, app: Optional[str] = None) -> Optional[CodeType]: app = session and session.app or app key = app + '/' + name if app is not None else name if key in cls.templates: return cls.templates[key] if app: module_file_name = (config.CACHE_PATH / 'apps' / app / name).with_suffix('.py') if not module_file_name.exists(): module_file_name = (config.CACHE_PATH / 'core' / name).with_suffix('.py') if not module_file_name.exists(): return None else: module_file_name = (config.CACHE_PATH / 'core' / name).with_suffix('.py') if not module_file_name.exists(): return None code = compile(module_file_name.read_text(encoding='utf-8'), str(module_file_name), 'exec') cls.templates[name] = code return code def build_node(self, template: HTMLTemplate, parent: Optional[RenderNode] = None) -> Optional[RenderNode]: raise NotImplementedError("All nodes are being built in derived component classes") def build(self): if self.__class__ is RendererCached: # override self class to ComponentRenderer derived class from the compiled context initial_locals = {k: v for k, v in self.ctx.locals.items() if k not in ('init', 'on_restart', 'ctx', 'refs')} common_locals = {'ctx': self.ctx, 'refs': self.ctx.refs, 'session': self.ctx.session, '_': self.ctx.session.gettext, 'logger': logger} self.ctx.locals.update(common_locals) exec(self.ctx.template, self.ctx.locals) self.ctx.locals.update(initial_locals) self.__class__ = self.ctx.locals[f'{self.ctx.name}Renderer'] self.build_node(self.ctx.template, self.ctx) def arrange_the_block(self, node): node.render_this_node = True self.ctx.shot += node #region Node updaters def _update_condition_node(self, node, recursive): state: int condition: Optional[Condition] for i, c in enumerate(node.conditions): if c.func(): state = i condition = c break else: state = -1 condition = None if node.state != state: if node.state >= 0: node.empty() node.state = state if state == -1: return False condition.template(node) return False return recursive def _update_loop_node(self, node: LoopNode, recursive): if not node.index_func: node.empty() empty = True iter = node.iterator() if iter: parentloop = self.ctx.locals.get('forloop') for_loop = ForLoopType(parentloop) self.ctx.locals['forloop'] = for_loop for i, value in enumerate(iter): empty = False for_loop.counter = i + 1 for_loop.counter0 = i node.loop_template(node, for_loop, value) if parentloop: self.ctx.locals['forloop'] = parentloop else: del self.ctx.locals['forloop'] else: empty = not node.index_map if empty: node.empty() iter = node.iterator() if iter: oldmap = node.index_map node.index_map = newmap = {} pos = 0 parentloop = self.ctx.locals.get('forloop') for_loop = ForLoopType(parentloop) self.ctx.locals['forloop'] = for_loop for i, value in enumerate(iter): for_loop.counter = i + 1 for_loop.counter0 = i index = node.index_func(for_loop, value) for_loop.index = index if index in oldmap: for sub in oldmap[index]: # type: RenderNode sub.move(pos) sub.rebind() pos += 1 newmap[index] = oldmap[index] del oldmap[index] else: newmap[index] = [] last_child_idx = len(node.children) node.loop_template(node, for_loop, value) for k in range(last_child_idx, len(node.children)): sub = node.children[k] sub.move(pos) newmap[index].append(sub) pos += 1 if parentloop: self.ctx.locals['forloop'] = parentloop else: del self.ctx.locals['forloop'] for lst in oldmap.values(): for sub in lst: sub.remove() lst.clear() empty = not node.index_map if empty and node.else_template: node.else_template(node) return False def _update_set_node(self, node: SetNode, recursive): node.variables.refresh() if node.scoped: self.ctx.scope.update(node.variables) else: self.ctx.locals.update(node.variables) if node.self_clear: node.empty() node.template(node, *node.variables.values()) #del self.ctx.locals[node.var_name] return False else: return recursive NODE_UPDATERS: dict[str, Callable[[Self, RenderNode, bool], bool]] = { 'HTMLElement': RendererHTML._update_html_node, 'NSElement': RendererHTML._update_html_node, 'Context': RendererHTML._update_context_node, 'TextNode': RendererHTML._update_text_node, 'ConditionNode': _update_condition_node, 'LoopNode': _update_loop_node, 'SetNode': _update_set_node, 'ReactNode': lambda *args: True, } #endregion def update(self, node: RenderNode, recursive: bool = False): if self.NODE_UPDATERS[typename(node)](self, node, recursive): self.update_children(node)