Source code for mode.utils.graphs.formatter
"""Formatting graphs."""
from typing import Any, Mapping
from mode.utils.objects import label
from mode.utils.types.graphs import GraphFormatterT, _T
def dedent_initial(s: str, n: int = 4) -> str:
"""Remove identation from first line of text."""
return s[n:] if s[:n] == ' ' * n else s
def dedent(s: str, n: int = 4, sep: str = '\n') -> str:
"""Remove identation."""
return sep.join(dedent_initial(l) for l in s.splitlines())
class DOT:
"""Constants related to the dot format."""
HEAD = dedent('''
{IN}{type} {id} {{
{INp}graph [{attrs}]
''')
ATTR = '{name}={value}'
NODE = '{INp}"{0}" [{attrs}]'
EDGE = '{INp}"{0}" {dir} "{1}" [{attrs}]'
ATTRSEP = ', '
DIRS = {'graph': '--', 'digraph': '->'}
TAIL = '{IN}}}'
[docs]class GraphFormatter(GraphFormatterT):
"""Format dependency graphs."""
_attr = DOT.ATTR.strip()
_node = DOT.NODE.strip()
_edge = DOT.EDGE.strip()
_head = DOT.HEAD.strip()
_tail = DOT.TAIL.strip()
_attrsep = DOT.ATTRSEP
_dirs = dict(DOT.DIRS)
scheme: Mapping[str, Any] = {
'shape': 'box',
'arrowhead': 'vee',
'style': 'filled',
'fontname': 'HelveticaNeue',
}
edge_scheme: Mapping[str, Any] = {
'color': 'darkseagreen4',
'arrowcolor': 'black',
'arrowsize': 0.7,
}
node_scheme: Mapping[str, Any] = {
'fillcolor': 'palegreen3',
'color': 'palegreen4',
}
term_scheme: Mapping[str, Any] = {
'fillcolor': 'palegreen1',
'color': 'palegreen2',
}
graph_scheme: Mapping[str, Any] = {
'bgcolor': 'mintcream',
}
def __init__(self,
root: Any = None,
type: str = None,
id: str = None,
indent: int = 0,
inw: str = ' ' * 4,
**scheme: Any) -> None:
self.id = id or 'dependencies'
self.root = root
self.type = type or 'digraph'
self.direction = self._dirs[self.type]
self.IN = inw * (indent or 0)
self.INp = self.IN + inw
self.scheme = dict(self.scheme, **scheme)
self.graph_scheme = dict(self.graph_scheme, root=self.label(self.root))
[docs] def attr(self, name: str, value: Any) -> str:
return self.FMT(self._attr, name=name, value=f'"{value}"')
[docs] def attrs(self, d: Mapping = None, scheme: Mapping = None) -> str:
scheme = {**self.scheme, **scheme} if scheme else self.scheme
d = {**scheme, **d} if d else scheme
return self._attrsep.join(
str(self.attr(k, v)) for k, v in d.items()
)
[docs] def head(self, **attrs: Any) -> str:
return self.FMT(
self._head, id=self.id, type=self.type,
attrs=self.attrs(attrs, self.graph_scheme),
)
[docs] def node(self, obj: _T, **attrs: Any) -> str:
return self.draw_node(obj, self.node_scheme, attrs)
[docs] def terminal_node(self, obj: _T, **attrs: Any) -> str:
return self.draw_node(obj, self.term_scheme, attrs)
def _enc(self, s: str) -> str:
return s.encode('utf-8', 'ignore').decode()
[docs] def FMT(self, fmt: str, *args: Any, **kwargs: Any) -> str:
return self._enc(fmt.format(
*args, **dict(kwargs, IN=self.IN, INp=self.INp)))
[docs] def draw_edge(self, a: _T, b: _T,
scheme: Mapping = None,
attrs: Mapping = None) -> str:
return self.FMT(
self._edge, self.label(a), self.label(b),
dir=self.direction, attrs=self.attrs(attrs, self.edge_scheme),
)
[docs] def draw_node(self, obj: _T,
scheme: Mapping = None,
attrs: Mapping = None) -> str:
return self.FMT(
self._node, self.label(obj), attrs=self.attrs(attrs, scheme),
)