html_compose.gallery.impl
1import weakref 2from collections.abc import Callable 3from typing import Any, Literal, NamedTuple 4 5from .. import render, resource 6from ..base_types import Node 7 8 9class ShowcaseEntry(NamedTuple): 10 fixtures: Any 11 kwargs: dict[str, Any] | None 12 name: str | None 13 category: str | None 14 tags: list[str] | None 15 env: str 16 17 18class ShowcaseResult(NamedTuple): 19 name: str | None 20 func_name: str 21 func_module: str 22 category: str | None 23 tags: list[str] | None 24 success: bool 25 result: str | Exception 26 env: str 27 28 29class ShowcaseEnvironment(NamedTuple): 30 css: list[resource.css_import | str] 31 js: list[resource.js_import | str] 32 fonts: list[resource.font_import_manual | resource.font_import_provider] 33 isolation: Literal["shadow-dom", "iframe", "none"] 34 parent: str | None 35 body_override: Node | None 36 parent_element: Node | None 37 38 39showcase_registry: weakref.WeakKeyDictionary[Callable, list[ShowcaseEntry]] = ( 40 weakref.WeakKeyDictionary() 41) 42env_registry: dict[str, ShowcaseEnvironment] = {} 43 44 45def resolve_environment(env_name: str) -> ShowcaseEnvironment: 46 """ 47 Resolve the environment by name, including inheritance. 48 """ 49 if env_name not in env_registry: 50 if env_name == "default": 51 # Default environment 52 return ShowcaseEnvironment( 53 css=[], 54 js=[], 55 fonts=[], 56 isolation="none", 57 parent=None, 58 body_override=None, 59 parent_element=None, 60 ) 61 62 raise ValueError(f"Environment '{env_name}' is not declared") 63 64 env = env_registry[env_name] 65 66 css: list[resource.css_import | str] = env.css 67 js: list[resource.js_import | str] = env.js 68 fonts: list[resource.font_import_manual | resource.font_import_provider] = ( 69 env.fonts 70 ) 71 parent = env.parent 72 parent_set = set() 73 body_inherited = None 74 parent_element_inherited = None 75 while parent: 76 # prevent infinite loop 77 if parent in parent_set: 78 raise ValueError( 79 f"Circular environment inheritance detected at '{parent}'" 80 ) 81 parent_set.add(parent) 82 83 # Resolve parent 84 parent_env = env_registry.get(parent) 85 if not parent_env: 86 raise ValueError(f"Environment '{parent}' is not declared") 87 88 # Add its resources 89 css = parent_env.css + css 90 js = parent_env.js + js 91 fonts = parent_env.fonts + fonts 92 # If we haven't previously inherited a body, take this one 93 if body_inherited is None and parent_env.body_override is not None: 94 body_inherited = parent_env.body_override 95 parent_element_inherited = parent_env.parent_element 96 97 parent = parent_env.parent 98 99 body_override = ( 100 env.body_override if env.body_override is not None else body_inherited 101 ) 102 parent_element = ( 103 env.parent_element 104 if env.parent_element is not None 105 else parent_element_inherited 106 ) 107 return ShowcaseEnvironment( 108 css=css, 109 js=js, 110 fonts=fonts, 111 isolation=env.isolation, 112 parent=env.parent, 113 body_override=body_override, 114 parent_element=parent_element, 115 ) 116 117 118def prepare(fn, showcase_entry: ShowcaseEntry) -> tuple[bool, str | Exception]: 119 """ 120 Prepare the function with the given showcase entry. 121 This may involve setting up the environment, injecting fixtures, etc. 122 """ 123 try: 124 env = resolve_environment(showcase_entry.env) 125 component = fn( 126 *showcase_entry.fixtures, **(showcase_entry.kwargs or {}) 127 ) 128 129 if env.body_override is not None and env.parent_element is not None: 130 env.parent_element.append(component) # type: ignore 131 try: 132 html = render(env.body_override) 133 finally: 134 env.parent_element._children.pop() # type: ignore 135 else: 136 html = render(component) 137 except Exception as e: 138 return False, e 139 return True, html 140 141 142def show_showcases( 143 filter_allows: Callable[[ShowcaseEntry], bool] | None = None, 144) -> list[ShowcaseResult]: 145 """ 146 Return all registered showcases, optionally filtered by a predicate. 147 """ 148 all_showcases: list[ShowcaseResult] = [] 149 for func, entries in showcase_registry.items(): 150 for entry in entries: 151 if filter_allows is None or filter_allows(entry): 152 success, result = prepare(func, entry) 153 all_showcases.append( 154 ShowcaseResult( 155 name=entry.name, 156 func_name=func.__qualname__, 157 func_module=func.__module__, 158 category=entry.category, 159 tags=entry.tags, 160 success=success, 161 result=result, 162 env=entry.env, 163 ) 164 ) 165 return all_showcases
class
ShowcaseEntry(typing.NamedTuple):
10class ShowcaseEntry(NamedTuple): 11 fixtures: Any 12 kwargs: dict[str, Any] | None 13 name: str | None 14 category: str | None 15 tags: list[str] | None 16 env: str
ShowcaseEntry(fixtures, kwargs, name, category, tags, env)
class
ShowcaseResult(typing.NamedTuple):
19class ShowcaseResult(NamedTuple): 20 name: str | None 21 func_name: str 22 func_module: str 23 category: str | None 24 tags: list[str] | None 25 success: bool 26 result: str | Exception 27 env: str
ShowcaseResult(name, func_name, func_module, category, tags, success, result, env)
class
ShowcaseEnvironment(typing.NamedTuple):
30class ShowcaseEnvironment(NamedTuple): 31 css: list[resource.css_import | str] 32 js: list[resource.js_import | str] 33 fonts: list[resource.font_import_manual | resource.font_import_provider] 34 isolation: Literal["shadow-dom", "iframe", "none"] 35 parent: str | None 36 body_override: Node | None 37 parent_element: Node | None
ShowcaseEnvironment(css, js, fonts, isolation, parent, body_override, parent_element)
ShowcaseEnvironment( css: list[html_compose.resource.css_import | str], js: list[html_compose.resource.js_import | str], fonts: list[html_compose.resource.font_import_manual | html_compose.resource.font_import_provider], isolation: Literal['shadow-dom', 'iframe', 'none'], parent: str | None, body_override: Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]], parent_element: Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]])
Create new instance of ShowcaseEnvironment(css, js, fonts, isolation, parent, body_override, parent_element)
body_override: Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[html_compose.base_types.ElementBase], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]]]
Alias for field number 5
parent_element: Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[html_compose.base_types.ElementBase], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]]]
Alias for field number 6
46def resolve_environment(env_name: str) -> ShowcaseEnvironment: 47 """ 48 Resolve the environment by name, including inheritance. 49 """ 50 if env_name not in env_registry: 51 if env_name == "default": 52 # Default environment 53 return ShowcaseEnvironment( 54 css=[], 55 js=[], 56 fonts=[], 57 isolation="none", 58 parent=None, 59 body_override=None, 60 parent_element=None, 61 ) 62 63 raise ValueError(f"Environment '{env_name}' is not declared") 64 65 env = env_registry[env_name] 66 67 css: list[resource.css_import | str] = env.css 68 js: list[resource.js_import | str] = env.js 69 fonts: list[resource.font_import_manual | resource.font_import_provider] = ( 70 env.fonts 71 ) 72 parent = env.parent 73 parent_set = set() 74 body_inherited = None 75 parent_element_inherited = None 76 while parent: 77 # prevent infinite loop 78 if parent in parent_set: 79 raise ValueError( 80 f"Circular environment inheritance detected at '{parent}'" 81 ) 82 parent_set.add(parent) 83 84 # Resolve parent 85 parent_env = env_registry.get(parent) 86 if not parent_env: 87 raise ValueError(f"Environment '{parent}' is not declared") 88 89 # Add its resources 90 css = parent_env.css + css 91 js = parent_env.js + js 92 fonts = parent_env.fonts + fonts 93 # If we haven't previously inherited a body, take this one 94 if body_inherited is None and parent_env.body_override is not None: 95 body_inherited = parent_env.body_override 96 parent_element_inherited = parent_env.parent_element 97 98 parent = parent_env.parent 99 100 body_override = ( 101 env.body_override if env.body_override is not None else body_inherited 102 ) 103 parent_element = ( 104 env.parent_element 105 if env.parent_element is not None 106 else parent_element_inherited 107 ) 108 return ShowcaseEnvironment( 109 css=css, 110 js=js, 111 fonts=fonts, 112 isolation=env.isolation, 113 parent=env.parent, 114 body_override=body_override, 115 parent_element=parent_element, 116 )
Resolve the environment by name, including inheritance.
119def prepare(fn, showcase_entry: ShowcaseEntry) -> tuple[bool, str | Exception]: 120 """ 121 Prepare the function with the given showcase entry. 122 This may involve setting up the environment, injecting fixtures, etc. 123 """ 124 try: 125 env = resolve_environment(showcase_entry.env) 126 component = fn( 127 *showcase_entry.fixtures, **(showcase_entry.kwargs or {}) 128 ) 129 130 if env.body_override is not None and env.parent_element is not None: 131 env.parent_element.append(component) # type: ignore 132 try: 133 html = render(env.body_override) 134 finally: 135 env.parent_element._children.pop() # type: ignore 136 else: 137 html = render(component) 138 except Exception as e: 139 return False, e 140 return True, html
Prepare the function with the given showcase entry. This may involve setting up the environment, injecting fixtures, etc.
def
show_showcases( filter_allows: Callable[[ShowcaseEntry], bool] | None = None) -> list[ShowcaseResult]:
143def show_showcases( 144 filter_allows: Callable[[ShowcaseEntry], bool] | None = None, 145) -> list[ShowcaseResult]: 146 """ 147 Return all registered showcases, optionally filtered by a predicate. 148 """ 149 all_showcases: list[ShowcaseResult] = [] 150 for func, entries in showcase_registry.items(): 151 for entry in entries: 152 if filter_allows is None or filter_allows(entry): 153 success, result = prepare(func, entry) 154 all_showcases.append( 155 ShowcaseResult( 156 name=entry.name, 157 func_name=func.__qualname__, 158 func_module=func.__module__, 159 category=entry.category, 160 tags=entry.tags, 161 success=success, 162 result=result, 163 env=entry.env, 164 ) 165 ) 166 return all_showcases
Return all registered showcases, optionally filtered by a predicate.