Skip to content

Instantly share code, notes, and snippets.

@Tishka17
Created April 10, 2024 12:16
Show Gist options
  • Save Tishka17/9d7c732707512f0a4589c32343d59fe0 to your computer and use it in GitHub Desktop.
Save Tishka17/9d7c732707512f0a4589c32343d59fe0 to your computer and use it in GitHub Desktop.
Dishka rendering example 2

Usage:

regs = [container.registry]+list(container.child_registries)
x = render(regs, type(container))
print(x)
from collections import defaultdict
from collections.abc import Iterable
from dataclasses import dataclass
from uuid import uuid4
from dishka.dependency_source.factory import Factory
from dishka.entities.key import DependencyKey
from dishka.entities.scope import BaseScope
from dishka.registry import Registry
@dataclass
class Class:
id: str
name: str
component: str
scope: BaseScope
dependencies: list[str]
final: bool = False
auto: bool = False
def factory_to_class(factory: Factory, keys: dict[DependencyKey, str],
container: type):
hint = factory.provides.type_hint
scope = factory.scope
if name := getattr(hint, "__name__", None):
name = getattr(hint, "__module__", None) + "\n" + name
else:
name = str(hint)
return Class(
id=keys[factory.provides],
name=name,
scope=scope,
component=factory.provides.component,
dependencies=[
keys[dep] for dep in factory.dependencies
],
auto=hint==container,
)
def render_class(cls: Class):
if cls.final:
return f""" {cls.id}(["⛳ {cls.name}"])\n"""
else:
return f""" {cls.id}["🧩 {cls.name}"]\n"""
def render_relations(cls: Class):
return "".join(
f" {dep} --> {cls.id}\n" for dep in cls.dependencies
)
def render_component(component: str, scope: BaseScope, classes: list[Class]):
res = "\n"
comp_id = component_id(scope, component)
res += f"style {comp_id} {COMPONENT_COLOR}\n"
res += f"""subgraph {comp_id}["Component {component!r}"]\n"""
for cls in classes:
if cls.auto:
continue
res +=render_class(cls)
res += "end\n"
return res
def component_id(scope: BaseScope, component: str):
return f"{component}_{scope.name}"
def render_scope(prev_scope: BaseScope | None, scope: BaseScope,
components: Iterable[str], color: str):
res = f"style {scope.name} {color}\n"
res += f"""subgraph {scope.name}["{scope}"]\n"""
for component in components:
comp_id = component_id(scope, component)
res += f" subgraph {comp_id}\n"
res += " end\n"
res += "end\n"
if prev_scope is not None:
res += f"{prev_scope.name} -.-> {scope.name}\n\n"
return res
SCOPE_COLORS = [
"fill:#FFCDD2,stroke:#EF9A9A",
"fill:#E1BEE7,stroke:#CE93D8",
"fill:#C5CAE9,stroke:#9FA8DA",
"fill:#B3E5FC,stroke:#81D4FA",
]
COMPONENT_COLOR = "fill:#ECEFF1,stroke:#B0BEC5"
def render(registries: list[Registry], container):
keys: dict[DependencyKey, str] = {}
all_classes: dict[Factory, Class] = {}
requested = set()
components: dict[tuple[str, BaseScope], list[Class]] = defaultdict(list)
for registry in registries:
for factory in registry.factories.values():
keys[factory.provides] = str(uuid4()).replace("-", "_")
res = "flowchart LR\n"
for registry in registries:
for factory in registry.factories.values():
cls = factory_to_class(factory, keys, container)
all_classes[factory] = cls
for dep in factory.dependencies:
requested.add(dep)
res += render_relations(cls)
component_key = factory.provides.component, factory.scope
components[component_key].append(cls)
for registry in registries:
for factory in registry.factories.values():
if factory.provides not in requested:
all_classes[factory].final = True
for (component, scope), classes in components.items():
res += render_component(component, scope, classes)
scopes: dict[BaseScope, list[str]] = defaultdict(list)
for component, scope in components:
scopes[scope].append(component)
prev_scope = None
for color, (scope, scomponents) in zip(SCOPE_COLORS, scopes.items(), strict=False):
res += render_scope(prev_scope, scope, scomponents, color)
prev_scope = scope
return res
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment