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)

ShowcaseEntry( fixtures: Any, kwargs: dict[str, typing.Any] | None, name: str | None, category: str | None, tags: list[str] | None, env: str)

Create new instance of ShowcaseEntry(fixtures, kwargs, name, category, tags, env)

fixtures: Any

Alias for field number 0

kwargs: dict[str, typing.Any] | None

Alias for field number 1

name: str | None

Alias for field number 2

category: str | None

Alias for field number 3

tags: list[str] | None

Alias for field number 4

env: str

Alias for field number 5

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)

ShowcaseResult( name: str | None, func_name: str, func_module: str, category: str | None, tags: list[str] | None, success: bool, result: str | Exception, env: str)

Create new instance of ShowcaseResult(name, func_name, func_module, category, tags, success, result, env)

name: str | None

Alias for field number 0

func_name: str

Alias for field number 1

func_module: str

Alias for field number 2

category: str | None

Alias for field number 3

tags: list[str] | None

Alias for field number 4

success: bool

Alias for field number 5

result: str | Exception

Alias for field number 6

env: str

Alias for field number 7

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)

Alias for field number 0

Alias for field number 1

isolation: Literal['shadow-dom', 'iframe', 'none']

Alias for field number 3

parent: str | None

Alias for field number 4

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

showcase_registry: weakref.WeakKeyDictionary[Callable, list[ShowcaseEntry]] = <WeakKeyDictionary>
env_registry: dict[str, ShowcaseEnvironment] = {}
def resolve_environment(env_name: str) -> ShowcaseEnvironment:
 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.

def prepare( fn, showcase_entry: ShowcaseEntry) -> tuple[bool, str | Exception]:
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.