from collections.abc import Iterable, Iterator
from typing import IO, Any

from typing_extensions import override

__all__ = ("DOT", "CycleError", "DependencyGraph", "GraphFormatter")

class DOT:
    ATTR: str
    ATTRSEP: str
    DIRS: dict[str, str]
    EDGE: str
    HEAD: str
    NODE: str
    TAIL: str

class CycleError(Exception):
    def __init__(self, node: Any) -> None: ...

class GraphFormatter:
    edge_scheme: dict[str, Any]
    graph_scheme: dict[str, Any]
    node_scheme: dict[str, Any]
    scheme: dict[str, Any]
    term_scheme: dict[str, Any]

    def __init__(
        self,
        root: Any | None = None,
        type: str | None = None,
        id: str | None = None,
        indent: int = 0,
        inw: str = "    ",
        **scheme: Any,
    ) -> None: ...
    def attr(self, name: str, value: Any) -> str: ...
    def attrs(self, d: dict[str, Any], scheme: dict[str, Any] | None = None) -> str: ...
    def draw_edge(
        self,
        a: Any,
        b: Any,
        scheme: dict[str, Any] | None = None,
        attrs: dict[str, Any] | None = None,
    ) -> str: ...
    def draw_node(
        self,
        obj: Any,
        scheme: dict[str, Any] | None = None,
        attrs: dict[str, Any] | None = None,
    ) -> str: ...
    def edge(self, a: Any, b: Any, **attrs: Any) -> str: ...
    def FMT(self, fmt: str, *args: Any, **kwargs: Any) -> str: ...
    def head(self, **attrs: Any) -> str: ...
    def label(self, obj: Any) -> str: ...
    def node(self, obj: Any, **attrs: Any) -> str: ...
    def tail(self) -> str: ...
    def terminal_node(self, obj: Any, **attrs: Any) -> str: ...

class DependencyGraph(dict[Any, Any]):
    def __init__(
        self, it: Iterable[Any] | None = None, formatter: GraphFormatter | None = None
    ) -> None: ...
    def add_arc(self, obj: Any) -> None: ...
    def add_edge(self, A: Any, B: Any) -> None: ...
    def connect(self, graph: DependencyGraph) -> None: ...
    def edges(self) -> list[tuple[Any, Any]]: ...
    def format(self, obj: Any) -> str: ...
    def iteritems(self) -> Iterator[tuple[Any, Any]]: ...
    def repr_node(self, obj: Any, level: int = 1, fmt: str = "{0}({1})") -> str: ...
    def to_dot(self, fh: IO[str], formatter: GraphFormatter | None = None) -> None: ...
    def topsort(self) -> list[Any]: ...
    @override
    def update(self, it: Iterable[Any]) -> None: ...  # type: ignore[override]  # pyright: ignore[reportIncompatibleMethodOverride]
    def valency_of(self, obj: Any) -> int: ...
