* feat(output): Generate dot file and support multi outformat.(#441) * [fix] forget to clean the dot generated file. * [fix] indentation * [fix] Review + add more cases in unittest * [fix] Add dot in the testtags/v0.21.1
@@ -40,7 +40,7 @@ def setcluster(cluster): | |||||
class Diagram: | class Diagram: | ||||
__directions = ("TB", "BT", "LR", "RL") | __directions = ("TB", "BT", "LR", "RL") | ||||
__curvestyles = ("ortho", "curved") | __curvestyles = ("ortho", "curved") | ||||
__outformats = ("png", "jpg", "svg", "pdf") | |||||
__outformats = ("png", "jpg", "svg", "pdf", "dot") | |||||
# fmt: off | # fmt: off | ||||
_default_graph_attrs = { | _default_graph_attrs = { | ||||
@@ -127,8 +127,13 @@ class Diagram: | |||||
raise ValueError(f'"{curvestyle}" is not a valid curvestyle') | raise ValueError(f'"{curvestyle}" is not a valid curvestyle') | ||||
self.dot.graph_attr["splines"] = curvestyle | self.dot.graph_attr["splines"] = curvestyle | ||||
if not self._validate_outformat(outformat): | |||||
raise ValueError(f'"{outformat}" is not a valid output format') | |||||
if isinstance(outformat, list): | |||||
for one_format in outformat: | |||||
if not self._validate_outformat(one_format): | |||||
raise ValueError(f'"{one_format}" is not a valid output format') | |||||
else: | |||||
if not self._validate_outformat(outformat): | |||||
raise ValueError(f'"{outformat}" is not a valid output format') | |||||
self.outformat = outformat | self.outformat = outformat | ||||
# Merge passed in attributes | # Merge passed in attributes | ||||
@@ -155,25 +160,13 @@ class Diagram: | |||||
return self.dot.pipe(format="png") | return self.dot.pipe(format="png") | ||||
def _validate_direction(self, direction: str) -> bool: | def _validate_direction(self, direction: str) -> bool: | ||||
direction = direction.upper() | |||||
for v in self.__directions: | |||||
if v == direction: | |||||
return True | |||||
return False | |||||
return direction.upper() in self.__directions | |||||
def _validate_curvestyle(self, curvestyle: str) -> bool: | def _validate_curvestyle(self, curvestyle: str) -> bool: | ||||
curvestyle = curvestyle.lower() | |||||
for v in self.__curvestyles: | |||||
if v == curvestyle: | |||||
return True | |||||
return False | |||||
return curvestyle.lower() in self.__curvestyles | |||||
def _validate_outformat(self, outformat: str) -> bool: | def _validate_outformat(self, outformat: str) -> bool: | ||||
outformat = outformat.lower() | |||||
for v in self.__outformats: | |||||
if v == outformat: | |||||
return True | |||||
return False | |||||
return outformat.lower() in self.__outformats | |||||
def node(self, nodeid: str, label: str, **attrs) -> None: | def node(self, nodeid: str, label: str, **attrs) -> None: | ||||
"""Create a new node.""" | """Create a new node.""" | ||||
@@ -188,7 +181,11 @@ class Diagram: | |||||
self.dot.subgraph(dot) | self.dot.subgraph(dot) | ||||
def render(self) -> None: | def render(self) -> None: | ||||
self.dot.render(format=self.outformat, view=self.show, quiet=True) | |||||
if isinstance(self.outformat, list): | |||||
for one_format in self.outformat: | |||||
self.dot.render(format=one_format, view=self.show, quiet=True) | |||||
else: | |||||
self.dot.render(format=self.outformat, view=self.show, quiet=True) | |||||
class Cluster: | class Cluster: | ||||
@@ -263,9 +260,8 @@ class Cluster: | |||||
def _validate_direction(self, direction: str): | def _validate_direction(self, direction: str): | ||||
direction = direction.upper() | direction = direction.upper() | ||||
for v in self.__directions: | |||||
if v == direction: | |||||
return True | |||||
if direction in self.__directions: | |||||
return True | |||||
return False | return False | ||||
def node(self, nodeid: str, label: str, **attrs) -> None: | def node(self, nodeid: str, label: str, **attrs) -> None: | ||||
@@ -44,7 +44,7 @@ diag | |||||
You can specify the output file format with `outformat` parameter. Default is **png**. | You can specify the output file format with `outformat` parameter. Default is **png**. | ||||
> (png, jpg, svg, and pdf) are allowed. | |||||
> (png, jpg, svg, pdf and dot) are allowed. | |||||
```python | ```python | ||||
from diagrams import Diagram | from diagrams import Diagram | ||||
@@ -54,6 +54,16 @@ with Diagram("Simple Diagram", outformat="jpg"): | |||||
EC2("web") | EC2("web") | ||||
``` | ``` | ||||
The `outformat` parameter also support list to output all the defined output in one call. | |||||
```python | |||||
from diagrams import Diagram | |||||
from diagrams.aws.compute import EC2 | |||||
with Diagram("Simple Diagram Multi Output", outformat=["jpg", "png", "dot"]): | |||||
EC2("web") | |||||
``` | |||||
You can specify the output filename with `filename` parameter. The extension shouldn't be included, it's determined by the `outformat` parameter. | You can specify the output filename with `filename` parameter. The extension shouldn't be included, it's determined by the `outformat` parameter. | ||||
```python | ```python | ||||
@@ -26,7 +26,7 @@ class DiagramTest(unittest.TestCase): | |||||
def test_validate_direction(self): | def test_validate_direction(self): | ||||
# Normal directions. | # Normal directions. | ||||
for dir in ("TB", "BT", "LR", "RL"): | |||||
for dir in ("TB", "BT", "LR", "RL", "tb"): | |||||
Diagram(direction=dir) | Diagram(direction=dir) | ||||
# Invalid directions. | # Invalid directions. | ||||
@@ -36,7 +36,7 @@ class DiagramTest(unittest.TestCase): | |||||
def test_validate_curvestyle(self): | def test_validate_curvestyle(self): | ||||
# Normal directions. | # Normal directions. | ||||
for cvs in ("ortho", "curved"): | |||||
for cvs in ("ortho", "curved", "CURVED"): | |||||
Diagram(curvestyle=cvs) | Diagram(curvestyle=cvs) | ||||
# Invalid directions. | # Invalid directions. | ||||
@@ -46,7 +46,7 @@ class DiagramTest(unittest.TestCase): | |||||
def test_validate_outformat(self): | def test_validate_outformat(self): | ||||
# Normal output formats. | # Normal output formats. | ||||
for fmt in ("png", "jpg", "svg", "pdf"): | |||||
for fmt in ("png", "jpg", "svg", "pdf", "PNG", "dot"): | |||||
Diagram(outformat=fmt) | Diagram(outformat=fmt) | ||||
# Invalid output formats. | # Invalid output formats. | ||||
@@ -108,6 +108,18 @@ class DiagramTest(unittest.TestCase): | |||||
Node("node1") | Node("node1") | ||||
self.assertTrue(os.path.exists(f"{self.name}.png")) | self.assertTrue(os.path.exists(f"{self.name}.png")) | ||||
def test_outformat_list(self): | |||||
"""Check that outformat render all the files from the list.""" | |||||
self.name = 'diagrams_image' | |||||
with Diagram(show=False, outformat=["dot", "png"]): | |||||
Node("node1") | |||||
# both files must exist | |||||
self.assertTrue(os.path.exists(f"{self.name}.png")) | |||||
self.assertTrue(os.path.exists(f"{self.name}.dot")) | |||||
# clean the dot file as it only generated here | |||||
os.remove(self.name + ".dot") | |||||
class ClusterTest(unittest.TestCase): | class ClusterTest(unittest.TestCase): | ||||
def setUp(self): | def setUp(self): | ||||