1"""Pipe DOT source code through ``unflatten``."""
2
3import pathlib
4import typing
5
6from ..encoding import DEFAULT_ENCODING
7from .. import _tools
8from .. import exceptions
9
10from . import execute
11
12__all__ = ['UNFLATTEN_BINARY', 'unflatten']
13
14UNFLATTEN_BINARY = pathlib.Path('unflatten')
15
16
17@_tools.deprecate_positional_args(supported_number=1)
18def unflatten(source: str,
19 stagger: typing.Optional[int] = None,
20 fanout: bool = False,
21 chain: typing.Optional[int] = None,
22 encoding: str = DEFAULT_ENCODING) -> str:
23 """Return DOT ``source`` piped through ``unflatten`` preprocessor as string.
24
25 Args:
26 source: DOT source to process
27 (improve layout aspect ratio).
28 stagger: Stagger the minimum length of leaf edges
29 between 1 and this small integer.
30 fanout: Fanout nodes with indegree = outdegree = 1
31 when staggering (requires ``stagger``).
32 chain: Form disconnected nodes into chains of up to this many nodes.
33 encoding: Encoding to encode unflatten stdin and decode its stdout.
34
35 Returns:
36 Decoded stdout of the Graphviz unflatten command.
37
38 Raises:
39 graphviz.RequiredArgumentError: If ``fanout`` is given
40 but no ``stagger``.
41 graphviz.ExecutableNotFound: If the Graphviz 'unflatten' executable
42 is not found.
43 graphviz.CalledProcessError: If the returncode (exit status)
44 of the unflattening 'unflatten' subprocess is non-zero.
45
46 See also:
47 Upstream documentation:
48 https://www.graphviz.org/pdf/unflatten.1.pdf
49 """
50 if fanout and stagger is None:
51 raise exceptions.RequiredArgumentError('fanout given without stagger')
52
53 cmd = [UNFLATTEN_BINARY]
54 if stagger is not None:
55 cmd += ['-l', str(stagger)]
56 if fanout:
57 cmd.append('-f')
58 if chain is not None:
59 cmd += ['-c', str(chain)]
60
61 proc = execute.run_check(cmd, input=source, encoding=encoding,
62 capture_output=True)
63 return proc.stdout