Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/wireviz/DataClasses.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-

import sys
from dataclasses import InitVar, dataclass, field
from enum import Enum, auto
from pathlib import Path
Expand Down Expand Up @@ -280,7 +280,7 @@ def __post_init__(self) -> None:
self.gauge = g

if self.gauge_unit is not None:
print(
sys.stderr.write(
f"Warning: Cable {self.name} gauge_unit={self.gauge_unit} is ignored because its gauge contains {u}"
)
if u.upper() == "AWG":
Expand All @@ -304,7 +304,7 @@ def __post_init__(self) -> None:
)
self.length = L
if self.length_unit is not None:
print(
sys.stderr.write(
f"Warning: Cable {self.name} length_unit={self.length_unit} is ignored because its length contains {u}"
)
self.length_unit = u
Expand Down
106 changes: 56 additions & 50 deletions src/wireviz/Harness.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-

import re
import sys
from collections import Counter
from dataclasses import dataclass
from itertools import zip_longest
from pathlib import Path
from typing import Any, List, Union
from typing import Any, List, Union, Optional

from graphviz import Graph

Expand All @@ -20,13 +21,12 @@
Tweak,
Side,
)
from wireviz.svgembed import embed_svg_images_file
from wireviz.svgembed import embed_svg_images
from wireviz.wv_bom import (
HEADER_MPN,
HEADER_PN,
HEADER_SPN,
bom_list,
component_table_entry,
generate_bom,
get_additional_component_table,
pn_info_string,
Expand Down Expand Up @@ -543,11 +543,11 @@ def typecheck(name: str, value: Any, expect: type) -> None:
f'( +)?{attr}=("[^"]*"|[^] ]*)(?(1)| *)', "", entry
)
if n_subs < 1:
print(
sys.stderr.write(
f"Harness.create_graph() warning: {attr} not found in {keyword}!"
)
elif n_subs > 1:
print(
sys.stderr.write(
f"Harness.create_graph() warning: {attr} removed {n_subs} times in {keyword}!"
)
continue
Expand All @@ -562,7 +562,7 @@ def typecheck(name: str, value: Any, expect: type) -> None:
# If attr not found, then append it
entry = re.sub(r"\]$", f" {attr}={value}]", entry)
elif n_subs > 1:
print(
sys.stderr.write(
f"Harness.create_graph() warning: {attr} overridden {n_subs} times in {keyword}!"
)

Expand Down Expand Up @@ -650,55 +650,61 @@ def svg(self):
graph = self.graph
return embed_svg_images(graph.pipe(format="svg").decode("utf-8"), Path.cwd())


def output(
self,
filename: (str, Path),
view: bool = False,
cleanup: bool = True,
fmt: tuple = ("html", "png", "svg", "tsv"),
self,
output_dir: Optional[Union[str, Path]],
output_name: Optional[Union[str, Path]],
formats: List[str] | tuple[str]
) -> None:
# graphical output
graph = self.graph
svg_already_exists = Path(
f"{filename}.svg"
).exists() # if SVG already exists, do not delete later
# graphical output
for f in fmt:
if f in ("png", "svg", "html"):
if f == "html": # if HTML format is specified,
f = "svg" # generate SVG for embedding into HTML
# SVG file will be renamed/deleted later
_filename = f"{filename}.tmp" if f == "svg" else filename
# TODO: prevent rendering SVG twice when both SVG and HTML are specified
graph.format = f
graph.render(filename=_filename, view=view, cleanup=cleanup)
# embed images into SVG output
if "svg" in fmt or "html" in fmt:
embed_svg_images_file(f"{filename}.tmp.svg")
# GraphViz output
if "gv" in fmt:
graph.save(filename=f"{filename}.gv")
# BOM output
bomlist = bom_list(self.bom())
if "tsv" in fmt:
open_file_write(f"{filename}.bom.tsv").write(tuplelist2tsv(bomlist))
if "csv" in fmt:
# TODO: implement CSV output (preferrably using CSV library)
print("CSV output is not yet supported")
# HTML output
if "html" in fmt:
generate_html_output(filename, bomlist, self.metadata, self.options)
# PDF output
if "pdf" in fmt:

if "csv" in formats:
# TODO: implement CSV output (preferably using CSV library)
sys.stderr.write("CSV output is not yet supported")
if "pdf" in formats:
# TODO: implement PDF output
print("PDF output is not yet supported")
# delete SVG if not needed
if "html" in fmt and not "svg" in fmt:
# SVG file was just needed to generate HTML
Path(f"{filename}.tmp.svg").unlink()
elif "svg" in fmt:
Path(f"{filename}.tmp.svg").replace(f"{filename}.svg")
sys.stderr.write("PDF output is not yet supported")

outputs = {}
if "svg" in formats or "html" in formats:
# embed images into SVG output
outputs["svg"] = embed_svg_images(graph.pipe(format="svg", encoding="utf8"))

if "png" in formats:
outputs["png"] = graph.pipe(format="png")

# GraphViz output
if "gv" in formats:
outputs["gv"] = graph.pipe(format="gv")

if "tsv" in formats or "html" in formats:
bomlist = bom_list(self.bom())
# BOM output
if "tsv" in formats:
outputs["tsv"] = tuplelist2tsv(bomlist)

# HTML output
if "html" in formats and "svg" in outputs:
outputs["html"] = generate_html_output(outputs["svg"], output_dir, bomlist, self.metadata, self.options)

# print to stdout or write files in order
for f in formats:
if f in outputs:
output = outputs[f]
if output_dir is None or output_name is None:
if isinstance(output, (bytes, bytearray)):
sys.stdout.buffer.write(output)
else:
sys.stdout.write(output)
else:
file = f"{output_dir}/{output_name}.{f}"
if isinstance(output, (bytes, bytearray)):
with open(file, "wb") as binary_file:
binary_file.write(output)
else:
with open(file, "w") as binary_file:
binary_file.write(output)

def bom(self):
if not self._bom:
Expand Down
4 changes: 2 additions & 2 deletions src/wireviz/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
__version__ = "0.4-dev"

CMD_NAME = "wireviz" # Lower case command and module name
APP_NAME = "WireViz" # Application name in texts meant to be human readable
APP_URL = "https://github.com/formatc1702/WireViz"
APP_NAME = "WireViz" # Application name in texts meant to be human-readable
APP_URL = "https://github.com/wireviz/WireViz"
12 changes: 0 additions & 12 deletions src/wireviz/svgembed.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,3 @@ def get_mime_subtype(filename: Union[str, Path]) -> str:
if mime_subtype in mime_subtype_replacements:
mime_subtype = mime_subtype_replacements[mime_subtype]
return mime_subtype


def embed_svg_images_file(
filename_in: Union[str, Path], overwrite: bool = True
) -> None:
filename_in = Path(filename_in).resolve()
filename_out = filename_in.with_suffix(".b64.svg")
filename_out.write_text(
embed_svg_images(filename_in.read_text(), filename_in.parent)
)
if overwrite:
filename_out.replace(filename_in)
30 changes: 17 additions & 13 deletions src/wireviz/wireviz.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def parse(
return_types (optional):
One of the supported return types (see above), or a tuple of multiple return types.
If set to None, no output is returned by the function.
output_formats (optional):
output_formats (Tuple[str], optional):
One of the supported output types (see above), or a tuple of multiple output formats.
If set to None, no files are generated.
output_dir (Path | str, optional):
Expand Down Expand Up @@ -87,15 +87,18 @@ def parse(

yaml_data, yaml_file = _get_yaml_data_and_path(inp)
if output_formats:
# need to write data to file, determine output directory and filename
output_dir = _get_output_dir(yaml_file, output_dir)
output_name = _get_output_name(yaml_file, output_name)
output_file = output_dir / output_name
if str(output_dir) == "-":
# write to stdout
output_dir = None
else:
# write to directory
output_dir = _get_output_dir(yaml_file, output_dir)
output_name = _get_output_name(yaml_file, output_name)

if yaml_file:
# if reading from file, ensure that input file's parent directory is included in image_paths
default_image_path = yaml_file.parent.resolve()
if not default_image_path in [Path(x).resolve() for x in image_paths]:
if default_image_path not in [Path(x).resolve() for x in image_paths]:
image_paths.append(default_image_path)

# define variables =========================================================
Expand Down Expand Up @@ -362,11 +365,11 @@ def alternate_type(): # flip between connector and cable/arrow
harness.add_bom_item(line)

if output_formats:
harness.output(filename=output_file, fmt=output_formats, view=False)
harness.output(formats=output_formats, output_dir=output_dir, output_name=output_name)

if return_types:
returns = []
if isinstance(return_types, str): # only one return type speficied
if isinstance(return_types, str): # only one return type specified
return_types = [return_types]

return_types = [t.lower() for t in return_types]
Expand All @@ -390,10 +393,11 @@ def _get_yaml_data_and_path(inp: Union[str, Path, Dict]) -> (Dict, Path):
# if no FileNotFoundError exception happens, get file contents
yaml_str = open_file_read(yaml_path).read()
except (FileNotFoundError, OSError) as e:
# if inp is a long YAML string, Pathlib will raise OSError: [Errno 63]
# if inp is a long YAML string, Pathlib will raise OSError: [Errno 63].
# when trying to expand and resolve it as a path.
# Catch this error, but raise any others
if type(e) is OSError and e.errno != 63:
# it can also raise OSError: [Errno 36] File name too long.
# Catch these errors, but raise any others.
if type(e) is OSError and e.errno != 63 and e.errno != 36:
raise e
# file does not exist; assume inp is a YAML string
yaml_str = inp
Expand All @@ -417,7 +421,7 @@ def _get_output_dir(input_file: Path, default_output_dir: Path) -> Path:
return output_dir.resolve()


def _get_output_name(input_file: Path, default_output_name: Path) -> str:
def _get_output_name(input_file: Path, default_output_name: Union[None, str]) -> str:
if default_output_name: # user-specified output name
output_name = default_output_name
else: # auto-determine appropriate output name
Expand All @@ -429,7 +433,7 @@ def _get_output_name(input_file: Path, default_output_name: Path) -> str:


def main():
print("When running from the command line, please use wv_cli.py instead.")
sys.stderr.write("When running from the command line, please use wv_cli.py instead.")


if __name__ == "__main__":
Expand Down
Loading