1"""Return the version number from running ``dot -V``.""" 
    2 
    3import logging 
    4import os 
    5import re 
    6import subprocess 
    7from typing import Final 
    8 
    9from . import dot_command 
    10from . import execute 
    11 
    12VERSION_PATTERN: Final = re.compile(r''' 
    13                                    graphviz[ ] 
    14                                    version[ ] 
    15                                    (\d+)\.(\d+) 
    16                                    (?:\.(\d+) 
    17                                      (?: 
    18                                        ~dev\.\d{8}\.\d{4} 
    19                                        | 
    20                                        \.(\d+) 
    21                                      )? 
    22                                    )? 
    23                                    [ ] 
    24                                    ''', re.VERBOSE) 
    25 
    26 
    27log = logging.getLogger(__name__) 
    28 
    29 
    30def version() -> tuple[int, ...]: 
    31    """Return the upstream version number tuple from ``stderr`` of ``dot -V``. 
    32 
    33    Returns: 
    34        Two, three, or four ``int`` version ``tuple``. 
    35 
    36    Raises: 
    37        graphviz.ExecutableNotFound: If the Graphviz executable is not found. 
    38        graphviz.CalledProcessError: If the exit status is non-zero. 
    39        RuntimeError: If the output cannot be parsed into a version number. 
    40 
    41    Example: 
    42        >>> doctest_mark_exe() 
    43        >>> import graphviz 
    44        >>> graphviz.version()  # doctest: +ELLIPSIS 
    45        (...) 
    46 
    47    Note: 
    48        Ignores the ``~dev.<YYYYmmdd.HHMM>`` portion of development versions. 
    49 
    50    See also: 
    51        Upstream release version entry format: 
    52        https://gitlab.com/graphviz/graphviz/-/blob/f94e91ba819cef51a4b9dcb2d76153684d06a913/gen_version.py#L17-20 
    53    """ 
    54    cmd: list[os.PathLike[str] | str] = [dot_command.DOT_BINARY, '-V'] 
    55    proc = execute.run_check(cmd, 
    56                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 
    57                             encoding='ascii') 
    58 
    59    ma = VERSION_PATTERN.search(proc.stdout) 
    60    if ma is None: 
    61        raise RuntimeError(f'cannot parse {cmd!r} output: {proc.stdout!r}') 
    62 
    63    return tuple(int(d) for d in ma.groups() if d is not None)