File size: 2,288 Bytes
b4f2238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""Business logic services."""

from __future__ import annotations

from collections.abc import Callable, Iterable
from dataclasses import dataclass
from typing import Any, TypeVar, cast


@dataclass(frozen=True)
class CodePlaceholderDefinition:
    key: str
    source: str
    category: str
    display_name: str = ""
    description: str = ""
    example_value: str = ""


class CodePlaceholderRegistry:
    _instance: CodePlaceholderRegistry | None = None
    _definitions: dict[str, CodePlaceholderDefinition]

    def __new__(cls) -> CodePlaceholderRegistry:
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._definitions = {}
        return cls._instance

    def register(self, definitions: Iterable[CodePlaceholderDefinition]) -> None:
        for definition in definitions:
            self._definitions.setdefault(definition.key, definition)

    def upsert(self, definitions: Iterable[CodePlaceholderDefinition]) -> None:
        for definition in definitions:
            self._definitions[definition.key] = definition

    def list_definitions(self) -> list[CodePlaceholderDefinition]:
        return sorted(self._definitions.values(), key=lambda d: d.key)

    def clear(self) -> None:
        self._definitions.clear()


T = TypeVar("T")


def expose_placeholders(
    *,
    keys: list[str],
    source: str,
    category: str,
    metadata: dict[str, dict[str, Any]] | None = None,
    description: str = "",
) -> Callable[[T], T]:
    def decorator(target: T) -> T:
        defs: list[CodePlaceholderDefinition] = []
        per_key = metadata or {}
        for key in keys:
            key_meta = per_key.get(key, {}) or {}
            defs.append(
                CodePlaceholderDefinition(
                    key=key,
                    source=source,
                    category=category,
                    display_name=key_meta.get("display_name") or "",
                    description=key_meta.get("description") or description or "",
                    example_value=key_meta.get("example_value") or "",
                )
            )

        cast(Any, target).__code_placeholder_definitions__ = defs
        CodePlaceholderRegistry().register(defs)
        return target

    return decorator