From 587b359fa0afefa6f2bdfd6113719e08b40749b7 Mon Sep 17 00:00:00 2001 From: Daniel Rojas Date: Mon, 18 Oct 2021 18:20:19 +0200 Subject: [PATCH] Outsource `set_dot_basics()` and `apply_dot_tweaks()` --- src/wireviz/Harness.py | 90 ++----------------------------------- src/wireviz/wv_gv_html.py | 95 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 87 deletions(-) diff --git a/src/wireviz/Harness.py b/src/wireviz/Harness.py index 8c07296..1cc1ec6 100644 --- a/src/wireviz/Harness.py +++ b/src/wireviz/Harness.py @@ -33,10 +33,12 @@ from wireviz.wv_bom import ( ) from wireviz.wv_colors import get_color_hex, translate_color from wireviz.wv_gv_html import ( + apply_dot_tweaks, gv_connector_loops, gv_node_component, html_line_breaks, remove_links, + set_dot_basics, ) from wireviz.wv_helper import ( flatten2d, @@ -145,27 +147,7 @@ class Harness: def create_graph(self) -> Graph: dot = Graph() - dot.body.append(f"// Graph generated by {APP_NAME} {__version__}\n") - dot.body.append(f"// {APP_URL}\n") - dot.attr( - "graph", - rankdir="LR", - ranksep="2", - bgcolor=wv_colors.translate_color(self.options.bgcolor, "HEX"), - nodesep="0.33", - fontname=self.options.fontname, - ) - dot.attr( - "node", - shape="none", - width="0", - height="0", - margin="0", # Actual size of the node is entirely determined by the label. - style="filled", - fillcolor=wv_colors.translate_color(self.options.bgcolor_node, "HEX"), - fontname=self.options.fontname, - ) - dot.attr("edge", style="bold", fontname=self.options.fontname) + set_dot_basics(dot, self.options) for connector in self.connectors.values(): # generate connector node @@ -212,7 +194,6 @@ class Harness: html = [] - wirehtml = [] # conductor table wirehtml.append('') @@ -405,70 +386,7 @@ class Harness: fillcolor=translate_color(bgcolor, "HEX"), ) - def typecheck(name: str, value: Any, expect: type) -> None: - if not isinstance(value, expect): - raise Exception( - f"Unexpected value type of {name}: Expected {expect}, got {type(value)}\n{value}" - ) - - # TODO?: Differ between override attributes and HTML? - if self.tweak.override is not None: - typecheck("tweak.override", self.tweak.override, dict) - for k, d in self.tweak.override.items(): - typecheck(f"tweak.override.{k} key", k, str) - typecheck(f"tweak.override.{k} value", d, dict) - for a, v in d.items(): - typecheck(f"tweak.override.{k}.{a} key", a, str) - typecheck(f"tweak.override.{k}.{a} value", v, (str, type(None))) - - # Override generated attributes of selected entries matching tweak.override. - for i, entry in enumerate(dot.body): - if isinstance(entry, str): - # Find a possibly quoted keyword after leading TAB(s) and followed by [ ]. - match = re.match( - r'^\t*(")?((?(1)[^"]|[^ "])+)(?(1)") \[.*\]$', entry, re.S - ) - keyword = match and match[2] - if keyword in self.tweak.override.keys(): - for attr, value in self.tweak.override[keyword].items(): - if value is None: - entry, n_subs = re.subn( - f'( +)?{attr}=("[^"]*"|[^] ]*)(?(1)| *)', "", entry - ) - if n_subs < 1: - print( - f"Harness.create_graph() warning: {attr} not found in {keyword}!" - ) - elif n_subs > 1: - print( - f"Harness.create_graph() warning: {attr} removed {n_subs} times in {keyword}!" - ) - continue - - if len(value) == 0 or " " in value: - value = value.replace('"', r"\"") - value = f'"{value}"' - entry, n_subs = re.subn( - f'{attr}=("[^"]*"|[^] ]*)', f"{attr}={value}", entry - ) - if n_subs < 1: - # If attr not found, then append it - entry = re.sub(r"\]$", f" {attr}={value}]", entry) - elif n_subs > 1: - print( - f"Harness.create_graph() warning: {attr} overridden {n_subs} times in {keyword}!" - ) - - dot.body[i] = entry - - if self.tweak.append is not None: - if isinstance(self.tweak.append, list): - for i, element in enumerate(self.tweak.append, 1): - typecheck(f"tweak.append[{i}]", element, str) - dot.body.extend(self.tweak.append) - else: - typecheck("tweak.append", self.tweak.append, str) - dot.body.append(self.tweak.append) + apply_dot_tweaks(dot, self.tweak) for mate in self.mates: if mate.shape[0] == "<" and mate.shape[-1] == ">": diff --git a/src/wireviz/wv_gv_html.py b/src/wireviz/wv_gv_html.py index 002c8ac..af11743 100644 --- a/src/wireviz/wv_gv_html.py +++ b/src/wireviz/wv_gv_html.py @@ -2,8 +2,9 @@ import re from itertools import zip_longest -from typing import List, Optional, Union +from typing import Any, List, Optional, Union +from wireviz import APP_NAME, APP_URL, __version__ from wireviz.DataClasses import Cable, Color, Component, Connector, Options from wireviz.wv_colors import get_color_hex, translate_color from wireviz.wv_helper import pn_info_string, remove_links @@ -356,3 +357,95 @@ def html_size_attr_dict(image): def html_line_breaks(inp): return remove_links(inp).replace("\n", "
") if isinstance(inp, str) else inp + + +def set_dot_basics(dot, options): + dot.body.append(f"// Graph generated by {APP_NAME} {__version__}\n") + dot.body.append(f"// {APP_URL}\n") + dot.attr( + "graph", + rankdir="LR", + ranksep="2", + bgcolor=translate_color(options.bgcolor, "HEX"), + nodesep="0.33", + fontname=options.fontname, + ) + dot.attr( + "node", + shape="none", + width="0", + height="0", + margin="0", # Actual size of the node is entirely determined by the label. + style="filled", + fillcolor=translate_color(options.bgcolor_node, "HEX"), + fontname=options.fontname, + ) + dot.attr("edge", style="bold", fontname=options.fontname) + + +def apply_dot_tweaks(dot, tweak): + def typecheck(name: str, value: Any, expect: type) -> None: + if not isinstance(value, expect): + raise Exception( + f"Unexpected value type of {name}: Expected {expect}, got {type(value)}\n{value}" + ) + + # TODO?: Differ between override attributes and HTML? + if tweak.override is not None: + typecheck("tweak.override", tweak.override, dict) + for k, d in tweak.override.items(): + typecheck(f"tweak.override.{k} key", k, str) + typecheck(f"tweak.override.{k} value", d, dict) + for a, v in d.items(): + typecheck(f"tweak.override.{k}.{a} key", a, str) + typecheck(f"tweak.override.{k}.{a} value", v, (str, type(None))) + + # Override generated attributes of selected entries matching tweak.override. + for i, entry in enumerate(dot.body): + if not isinstance(entry, str): + continue + # Find a possibly quoted keyword after leading TAB(s) and followed by [ ]. + match = re.match(r'^\t*(")?((?(1)[^"]|[^ "])+)(?(1)") \[.*\]$', entry, re.S) + keyword = match and match[2] + if not keyword in tweak.override.keys(): + continue + + for attr, value in tweak.override[keyword].items(): + if value is None: + entry, n_subs = re.subn( + f'( +)?{attr}=("[^"]*"|[^] ]*)(?(1)| *)', "", entry + ) + if n_subs < 1: + print( + f"Harness.create_graph() warning: {attr} not found in {keyword}!" + ) + elif n_subs > 1: + print( + f"Harness.create_graph() warning: {attr} removed {n_subs} times in {keyword}!" + ) + continue + + if len(value) == 0 or " " in value: + value = value.replace('"', r"\"") + value = f'"{value}"' + entry, n_subs = re.subn( + f'{attr}=("[^"]*"|[^] ]*)', f"{attr}={value}", entry + ) + if n_subs < 1: + # If attr not found, then append it + entry = re.sub(r"\]$", f" {attr}={value}]", entry) + elif n_subs > 1: + print( + f"Harness.create_graph() warning: {attr} overridden {n_subs} times in {keyword}!" + ) + + dot.body[i] = entry + + if tweak.append is not None: + if isinstance(tweak.append, list): + for i, element in enumerate(tweak.append, 1): + typecheck(f"tweak.append[{i}]", element, str) + dot.body.extend(tweak.append) + else: + typecheck("tweak.append", tweak.append, str) + dot.body.append(tweak.append)